<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gary Byrne</title>
    <description>The latest articles on DEV Community by Gary Byrne (@garybyrne).</description>
    <link>https://dev.to/garybyrne</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F571681%2Fc69d3dfc-ed43-4e61-9bbc-f9c5103506e1.png</url>
      <title>DEV Community: Gary Byrne</title>
      <link>https://dev.to/garybyrne</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/garybyrne"/>
    <language>en</language>
    <item>
      <title>Introduction to ARIA Live Regions</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Thu, 25 Feb 2021 14:04:07 +0000</pubDate>
      <link>https://dev.to/garybyrne/introduction-to-aria-live-regions-2m0g</link>
      <guid>https://dev.to/garybyrne/introduction-to-aria-live-regions-2m0g</guid>
      <description>&lt;p&gt;In this article, we will look at ARIA live regions and how they can be used to expose information to assistive technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are ARIA live regions?
&lt;/h2&gt;

&lt;p&gt;According to the  &lt;a href="https://www.w3.org/TR/wai-aria-1.1/"&gt;W3C&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Live regions are perceivable regions of a web page that are typically updated as a result of an external event when user focus may be elsewhere.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In applications where content can update dynamically, users of assistive technologies may need to be updated about changes in those live regions. When we think of &lt;a href="https://uiowa.instructure.com/courses/40/pages/accessibility-principles-pou"&gt;POUR&lt;/a&gt; we want our content to be perceivable. It is important that we ensure our webpage content can be identified even when it changes. &lt;/p&gt;

&lt;p&gt;These changes in content can come from various sources. It could be the result of an API call to a third party that returned some data, a chat message appearing on screen, some user interaction, some AJAX update and much more. &lt;/p&gt;

&lt;p&gt;The MDN  &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions"&gt;docs&lt;/a&gt;  state that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dynamic content which updates without a page reload is generally either a region or a widget. Simple content changes which are not interactive should be marked as live regions. WAI-ARIA provides us with a list of live region roles and attributes that allows us as authors to mark content as live regions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.w3.org/TR/wai-aria-1.1/#attrs_liveregions"&gt;Live region attributes&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions"&gt;ARIA live region&lt;/a&gt;  attributes provide a way for us as authors to mark any element as a live region. By doing so, assistive technologies such as screen readers will announce dynamic changes to their content even when we no longer have focus on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  aria-live
&lt;/h3&gt;

&lt;p&gt;This is an extremely important aria attribute.&lt;br&gt;
By using the &lt;strong&gt;aria-live&lt;/strong&gt; attribute on an element, we are marking it as a live region.&lt;/p&gt;

&lt;p&gt;This attribute takes a priority setting as its value which tells the screen reader how it should announce changes to updated content. The value it takes for its priority setting can be one of the following:&lt;/p&gt;
&lt;h4&gt;
  
  
  off (default)
&lt;/h4&gt;

&lt;p&gt;This is the default value for every element and is the same as if the attribute was omitted entirely. Users will not be notified of changes to any elements content and the element will not be marked as a live region. The only reason you should set this value is if you want to overwrite the &lt;code&gt;aria-live&lt;/code&gt; value provided by an ARIA role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="off"&amp;gt;
//not a live region
&amp;lt;/div&amp;gt;
// is the same as 
&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  polite
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;polite&lt;/code&gt; value will mark the element as a live region and will ask the screen reader to announce changes to its content. However, it will not immediately announce it as it will wait until the screen reader is idle and no more announcements are taking place before doing so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="polite"&amp;gt;
//live region content
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  assertive
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;assertive&lt;/code&gt; value will mark the element as a live region and will announce the updated changes immediately as they happen and force the screen reader to stop its current operation. This will disrupt the current announcement of the screen reader and may very annoying for some users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="assertive"&amp;gt;
//live region content
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is noted on the MDN &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions"&gt;docs&lt;/a&gt; that&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the assertive value should only be used for time-sensitive/critical notifications that absolutely require the user's immediate attention.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  aria-relevant
&lt;/h3&gt;

&lt;p&gt;We can tell assistive technologies that only certain content is relevant to be announced on updates. We can do this by using the &lt;code&gt;aria-relevant&lt;/code&gt; attribute. This attribute can take any number of the following four values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;additions&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The addition of nodes within the live region is relevant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="polite" aria-relevant="additions"&amp;gt;
    // live region content.
   // only node additions are relevant
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;removals&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The removal of nodes within the live region is relevant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="polite" aria-relevant="removals"&amp;gt;
    // live region content.
   // only node removals are relevant
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;text&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Changes to the text content of existing nodes within the live region are relevant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="assertive" aria-relevant="text"&amp;gt;
    // live region content.
   // only text changes are relevant
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;all&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the same as using &lt;code&gt;additions removals text&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="polite" aria-relevant="all"&amp;gt;
    // live region content.
    // all changes are relevant
&amp;lt;/div&amp;gt;
// is the same as 
&amp;lt;div aria-live="polite" aria-relevant="additions removals text"&amp;gt;
    // live region content.
    // all changes are relevant
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: When content is irrelevant, then it will not be announced by the screen reader which is the same as if we applied &lt;code&gt;aria-live&lt;/code&gt; directly on that element. &lt;/p&gt;

&lt;p&gt;The default value for this attribute is &lt;code&gt;additions text&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div aria-live="polite" aria-relevant="additions text"&amp;gt;
    // live region content.
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The W3C have  &lt;a href="https://www.w3.org/TR/wai-aria-1.1/#aria-relevant"&gt;stated&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;when the aria-relevant attribute is not provided, the default value, additions text, indicates that text modifications and node additions are relevant, but those node removals are irrelevant. &lt;code&gt;aria-relevant&lt;/code&gt; values of removals or all are to be used sparingly. Assistive technologies only need to be informed of content removal when its removal represents an important change, such as a buddy leaving a chat room.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  aria-atomic
&lt;/h3&gt;

&lt;p&gt;When we have a live region and we know what content is relevant, the screen reader will announce what content has been updated. In some cases we want it to announce the entire live region even if it only has a small section of content that actually changed. This attribute takes a boolean (true or false). The default value for the &lt;code&gt;aria-atomic&lt;/code&gt; attribute is &lt;code&gt;false&lt;/code&gt; which means the screen reader only announces the updated content and not the content of the entire live region. In a lot of cases, this is perfectly fine but sometimes we may need &lt;code&gt;aria-atomic&lt;/code&gt; to be set to &lt;code&gt;true&lt;/code&gt; in order for us to get more context about the updated content.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;The following is an example using &lt;code&gt;aria-live&lt;/code&gt;,  &lt;code&gt;aria-relevant&lt;/code&gt; and &lt;code&gt;aria-atomic&lt;/code&gt;. See the MDN &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions"&gt;docs&lt;/a&gt; for more examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p aria-live="polite"&amp;gt;
    The Quiz leader is Gary Byrne with a score of &amp;lt;span id="score"&amp;gt;70&amp;lt;/span&amp;gt; points.
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This element has an &lt;code&gt;aria-atomic&lt;/code&gt; value of &lt;code&gt;false&lt;/code&gt; by default. If we try to update the score from &lt;code&gt;70&lt;/code&gt; to &lt;code&gt;80&lt;/code&gt; points then the screen reader only announces the change to a score of 80. The screen reader user may be confused as the announcement will not include the person's name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p aria-live="polite" aria-atomic="true"&amp;gt;
    The Quiz leader is Gary Byrne with a score of &amp;lt;span id="score"&amp;gt;70&amp;lt;/span&amp;gt; points.
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that &lt;code&gt;aria-atomic&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt; it will announce &lt;code&gt;The Quiz leader is Gary Byrne with a score of 80 points&lt;/code&gt; whenever the score updates  &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/YzpYRae?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  aria-busy
&lt;/h3&gt;

&lt;p&gt;The last attribute I want to talk about is &lt;code&gt;aria-busy&lt;/code&gt;. If an element has a tonne of updates/modifications, we can use this attribute to tell the screen reader to pause live region announcements until these updates are completed. By default, the value for &lt;code&gt;aria-busy&lt;/code&gt; is false which means it will try to announce every single update.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When aria-busy is true for an element, assistive technologies MAY ignore changes to content owned by that element and then process all changes made during the busy period as a single, atomic update when aria-busy becomes false - &lt;a href="https://www.digitala11y.com/aria-busy-state/"&gt;Digital A11Y&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.w3.org/TR/wai-aria-1.1/#live_region_roles"&gt;Live region roles&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Live region roles are essentially ARIA roles that create live regions and can be changes by the above live region attributes. You can read more about these roles on the W3 &lt;a href="https://www.w3.org/TR/wai-aria-1.1/#live_region_roles"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Implicit aria-live&lt;/th&gt;
&lt;th&gt;Implicit aria-atomic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#alert"&gt;alert&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;assertive&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#status"&gt;status&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;polite&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#marquee"&gt;marquee&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#log"&gt;log&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;polite&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#timer"&gt;timer&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this blog and would like to see similar content in the future, or would like to get in touch then please follow me on &lt;a href="https://dev.to/garybyrne"&gt;Dev.to&lt;/a&gt;, &lt;a href="https://twitter.com/garybyrne1995"&gt;Twitter&lt;/a&gt; and &lt;a href="https://hashnode.garybyrne.codes"&gt;Hashnode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>aria</category>
      <category>html</category>
    </item>
    <item>
      <title>Understanding ARIA Landmarks - General Principles and Roles</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Mon, 15 Feb 2021 11:48:16 +0000</pubDate>
      <link>https://dev.to/garybyrne/understanding-aria-landmarks-general-principles-and-roles-10be</link>
      <guid>https://dev.to/garybyrne/understanding-aria-landmarks-general-principles-and-roles-10be</guid>
      <description>&lt;p&gt;ARIA landmarks are a set of ARIA roles that are provided to our HTML elements in order for us to identify the structure of a web page. When we visit a webpage, we get a sense of its visual structure from the beginning but this structure should also be present in our markup.&lt;/p&gt;

&lt;p&gt;We should use ARIA landmarks for a number of reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We are getting semantic markup that represents our visual structure. Perceivable content should have a semantically meaningful landmark.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keyboard navigation is supported for screen reader users. Screen reader users can navigate the web page based on its page structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can create skip links that can enhance keyboard navigation for both screen reader users and sighted users. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ARIA General Principles
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.w3.org" rel="noopener noreferrer"&gt;World Wide Web Consortium (W3C)&lt;/a&gt; has provided us with some &lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/index.html" rel="noopener noreferrer"&gt;ARIA landmarks general principles&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1: Identify the logical structure of a web page
&lt;/h3&gt;

&lt;p&gt;What this means is that we should really think about the structure of our web page. We should break our page into perceivable areas. We can also break our page into further sub-areas if needed. &lt;/p&gt;

&lt;h3&gt;
  
  
  2: Assign landmark roles to each area
&lt;/h3&gt;

&lt;p&gt;We should assign landmark roles to each area of our web page. Roles such as &lt;strong&gt;banner&lt;/strong&gt;, &lt;strong&gt;main&lt;/strong&gt;, &lt;strong&gt;complementary&lt;/strong&gt; and &lt;strong&gt;contentinfo&lt;/strong&gt; should be top/root level landmarks.&lt;/p&gt;

&lt;h3&gt;
  
  
  3: Label each area
&lt;/h3&gt;

&lt;p&gt;This principle is very important especially for users who are using assistive technologies. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a webpage has multiple landmarks in its markup, then the label for each landmark should be unique.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of this is when we have multiple &lt;code&gt;navigation&lt;/code&gt; landmarks. We could have primary and secondary navigations on our webpage. So we should give each a unique label. The first navigation should have an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute" rel="noopener noreferrer"&gt;aria-label attribute&lt;/a&gt; with a value of &lt;code&gt;primary&lt;/code&gt;. The secondary navigation should have an aria label of &lt;code&gt;secondary&lt;/code&gt; or whatever makes sense in that context. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If a landmark area begins with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements" rel="noopener noreferrer"&gt;heading level element&lt;/a&gt; then we can provide an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute" rel="noopener noreferrer"&gt;aria-labelledby attribute&lt;/a&gt; to that area.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Screen readers announce the type of landmark we are currently visiting. Therefore we can omit the landmark from any labels provided to the element. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of this can be seen above when we use &lt;code&gt;Primary&lt;/code&gt; instead of &lt;code&gt;Primary Navigation&lt;/code&gt; in the label. If we kept &lt;code&gt;Primary Navigation&lt;/code&gt; then the screen reader would announce &lt;code&gt;Primary Navigation Navigation&lt;/code&gt; which may be confusing for the user. &lt;/p&gt;

&lt;p&gt;Here is some examples of these types of labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;nav role="navigation" aria-label="primary"&amp;gt;
   // code here
&amp;lt;/nav&amp;gt;
&amp;lt;nav role="navigation" aria-label="secondary"&amp;gt;
   // code here
&amp;lt;/nav&amp;gt;
&amp;lt;aside role="complementary" aria-labelledby="notice"&amp;gt;
 &amp;lt;h2 id="notice"&amp;gt;Site Notice&amp;lt;/h2&amp;gt;
&amp;lt;/side&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A list of ARIA landmarks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Top-level landmarks
&lt;/h3&gt;

&lt;p&gt;Top-level landmarks stand for landmarks that are not contained within any other landmarks. For each of these landmarks, the ARIA landmark design principles are worth reading.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/banner.html" rel="noopener noreferrer"&gt;banner&lt;/a&gt;  - this represents &lt;strong&gt;site-oriented&lt;/strong&gt; content such as a logo or site search. This role should appear at the start of each web page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/contentinfo.html" rel="noopener noreferrer"&gt;contentinfo&lt;/a&gt; - this is to represent the &lt;strong&gt;footer&lt;/strong&gt; of a webpage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/main.html" rel="noopener noreferrer"&gt;main&lt;/a&gt; - this is to represent the &lt;strong&gt;primary content&lt;/strong&gt; of a webpage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/complementary.html" rel="noopener noreferrer"&gt;complementary&lt;/a&gt; - represents the &lt;strong&gt;supporting content&lt;/strong&gt; of a web page.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other landmarks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;navigation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;region&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;form&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;application&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;search&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read about all of these &lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/landmarks/HTML5.html" rel="noopener noreferrer"&gt;ARIA landmarks&lt;/a&gt; on the W3 website.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML sectioning elements
&lt;/h2&gt;

&lt;p&gt;Before HTML5 was introduced, we would have had to apply these ARIA landmarks to elements like &lt;code&gt;divs&lt;/code&gt; in order for us to convey the structure of the webpage in our markup. &lt;/p&gt;

&lt;p&gt;For example, a header section might look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div role="banner" id="header"&amp;gt;
  // site-oriented content goes here
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTML5 introduced some new &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_HTML_sections_and_outlines" rel="noopener noreferrer"&gt;sectioning elements&lt;/a&gt; that provide ARIA landmarks by default.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Element&lt;/th&gt;
&lt;th&gt;Default Landmark Role&lt;/th&gt;
&lt;th&gt;case&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;main&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;always&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nav&lt;/td&gt;
&lt;td&gt;&lt;code&gt;navigation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;always&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aside&lt;/td&gt;
&lt;td&gt;&lt;code&gt;complementary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;always&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;header&lt;/td&gt;
&lt;td&gt;&lt;code&gt;banner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;only when top level element  *&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;footer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;contentinfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;only when top level element *&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;footer&lt;/code&gt; element no longer has the &lt;code&gt;contentinfo&lt;/code&gt; role and the &lt;code&gt;header&lt;/code&gt; element no longer has the &lt;code&gt;banner&lt;/code&gt; role when they are descendants of &lt;code&gt;section&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;nav&lt;/code&gt;, &lt;code&gt;aside&lt;/code&gt; or &lt;code&gt;article&lt;/code&gt; elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;section&lt;/code&gt; element has the role of &lt;code&gt;region&lt;/code&gt; and the &lt;code&gt;form&lt;/code&gt; element has the role of &lt;code&gt;form&lt;/code&gt; only when they have an accessible label by using the &lt;code&gt;aria-label&lt;/code&gt;, &lt;code&gt;aria-labelledby&lt;/code&gt; or &lt;code&gt;title&lt;/code&gt; attribute. Some of these labeling methods were mentioned above.&lt;/p&gt;

&lt;p&gt;Let's see this in action.&lt;/p&gt;

&lt;p&gt;If we visit the homepage of the &lt;a href="https://css-tricks.com/" rel="noopener noreferrer"&gt;CSS Tricks website&lt;/a&gt; and open up our browser developer tools and navigate to its elements panel. Press Cmd + Option + C (Mac) or Ctrl + Shift + C (Windows, Linux, Chrome OS) to open it up. &lt;/p&gt;

&lt;p&gt;This is what you should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1613385873373%2FYP-MckYSO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1613385873373%2FYP-MckYSO.png" alt="The CSS tricks home page opened up on chrome dev tools elements tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can see a few elements in the elements panel. If we inspect the page we can see some sectioning elements such as &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;section&lt;/code&gt;, and &lt;code&gt;footer&lt;/code&gt;. If we inspect the header, we can't see anything about its role without viewing its accessibility information.&lt;/p&gt;

&lt;p&gt;To do this, we need to visit the accessibility panel for that element. This will be on the bottom menu where we have our styles.&lt;/p&gt;

&lt;p&gt;The menu will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1613386144119%2FfkvdvFj9L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1613386144119%2FfkvdvFj9L.png" alt="The bottom menu panel in the chrome dev tools elements tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;Accessibility&lt;/code&gt; tab. Do you notice anything about its role?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1613386246498%2FMhfQEGRiO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1613386246498%2FMhfQEGRiO.png" title="Header element role of banner" alt="The accessibility tab in chrome dev tools showing the header element with a default role of banner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that by default it is given the role of &lt;code&gt;banner&lt;/code&gt;. This is because the &lt;code&gt;header&lt;/code&gt; element has a role of banner provided already by this element when it's in the context of the body element. &lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;At the time of writing, Internet Explorer (IE) 11, 10, and 9 support all sectioning elements except the &lt;code&gt;main&lt;/code&gt; element which only has &lt;a href="https://caniuse.com/html5semantic" rel="noopener noreferrer"&gt;partial support for sectioning elements&lt;/a&gt;. Support on other browsers is relatively good but it's best to refer to the caniuse website for more information.&lt;/p&gt;

&lt;p&gt;Landmarks are supported by most major screen readers and Scott O'Hara wrote a really great article about &lt;a href="https://www.scottohara.me/blog/2018/03/03/landmarks.html" rel="noopener noreferrer"&gt;Accessible Landmarks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For all elements, if you need to support IE version 8 or below then you will need to style each element as a &lt;code&gt;block&lt;/code&gt; level element and use some JavaScript to create the element if it is not supported.&lt;/p&gt;

&lt;p&gt;Depending on your browser support requirements, it is suggested to manually test the browsers you support and use various screen readers in each test. &lt;/p&gt;

&lt;p&gt;If you enjoyed this blog and would like to see similar content in the future, or would like to get in touch then please follow me on &lt;a href="https://dev.to/garybyrne"&gt;Dev.to&lt;/a&gt;, &lt;a href="https://twitter.com/garybyrne1995" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://hashnode.garybyrne.codes" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating a GraphQL Content Hub with Apollo Server</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Sat, 13 Feb 2021 15:24:13 +0000</pubDate>
      <link>https://dev.to/garybyrne/creating-an-apollo-server-graphql-content-hub-721</link>
      <guid>https://dev.to/garybyrne/creating-an-apollo-server-graphql-content-hub-721</guid>
      <description>&lt;h1&gt;
  
  
  Creating a GraphQL Content Hub with Apollo Server and Deploying to Netlify
&lt;/h1&gt;

&lt;p&gt;In this tutorial, I am going to be showing you how to create a very simple version of a GraphQL content hub using &lt;a href="https://www.apollographql.com/docs/apollo-server/"&gt;Apollo Server&lt;/a&gt;.  We will be deploying this content hub to &lt;a href="//www.netlify.com"&gt;Netlify&lt;/a&gt; as a serverless function.&lt;/p&gt;

&lt;p&gt;Now that we know what we are making, let's jump right in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Apollo Server
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.apollographql.com/docs/apollo-server/"&gt;Apollo Server&lt;/a&gt; is a really cool open-source GraphQL server that works which pretty much any GraphQL client out there. In this article, we will be setting up an Apollo Server which runs a GraphQl &lt;a href="https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/"&gt;playground&lt;/a&gt; and we will have it hosted on &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a GraphQL Playground?
&lt;/h2&gt;

&lt;p&gt;This is essentially a browser-based IDE created by &lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt;. It makes it easy for us to interact with our data sources and query them all within the browser. Apollo has an entire section on the playground &lt;a href="https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/"&gt;here&lt;/a&gt; which is worth checking out. &lt;/p&gt;

&lt;p&gt;The GraphQL playground looks like &lt;a href="https://cdn.hashnode.com/res/hashnode/image/upload/v1612472082507/r7LAqoBHl.png"&gt;this&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---IE-yx1Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612472082507/r7LAqoBHl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---IE-yx1Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612472082507/r7LAqoBHl.png" alt="playground.png" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly are we building?
&lt;/h2&gt;

&lt;p&gt;We are building an Apollo GraphQL Server which acts as a &lt;strong&gt;content hub&lt;/strong&gt; serving data from various different sources. These sources can be anything from local data, third party APIs, or even a database. In the case of this tutorial, we will be pulling in data from local JSON, two third-party GraphQL APIs, and a third-party REST API.&lt;/p&gt;

&lt;p&gt;The data sources used in this project are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;a href="https://gist.github.com/garyb1/a3d1f6f35a711c28289ddde285b61dd2"&gt;books.json&lt;/a&gt; file stored in the codebase&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Rick and Morty GraphQL &lt;a href="https://rickandmortyapi.com/graphql"&gt;API&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OMDB Rest &lt;a href="http://www.omdbapi.com/"&gt;API&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trevorblades Countries GraphQL &lt;a href="https://github.com/trevorblades/countries"&gt;API&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Project setup
&lt;/h3&gt;

&lt;p&gt;Setting up a GraphQL API might sound more daunting than it actually is. Let's create our API step by step. &lt;/p&gt;

&lt;h4&gt;
  
  
  Dependencies needed
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;apollo-server-lambda&lt;/li&gt;
&lt;li&gt;graphql&lt;/li&gt;
&lt;li&gt;netlify-lambda (dev dependency)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Project structure
&lt;/h4&gt;

&lt;p&gt;Clone this &lt;a href="https://github.com/garyb1/netlify-apollo-server-starter"&gt;repo&lt;/a&gt; to get started quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/garyb1/netlify-apollo-server-starter
cd netlify-apollo-server-starter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install project dependencies.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Things to note:&lt;/p&gt;

&lt;p&gt;Inside &lt;strong&gt;src&lt;/strong&gt; we have a lambda directory. This is our folder for our serverless functions. We have a serverless function called &lt;code&gt;graphql&lt;/code&gt; that will run our Apollo Server.&lt;/p&gt;

&lt;p&gt;We have a &lt;code&gt;netlify.toml&lt;/code&gt; file with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[build]
  command = "npm run build"
  functions = "built-lambda"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we are building our application run the command &lt;code&gt;npm run build&lt;/code&gt;. &lt;code&gt;netlify-lambda&lt;/code&gt; builds to the &lt;code&gt;built lambda&lt;/code&gt; folder at the root of our project and Netlify reads functions from here. So when we run a build our &lt;code&gt;graphql.js&lt;/code&gt; serverless function will be placed in &lt;code&gt;/built-lambda&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up our schema and resolvers
&lt;/h2&gt;

&lt;p&gt;In our project template, you will see two files inside &lt;code&gt;src&lt;/code&gt;. These are &lt;code&gt;schema.js&lt;/code&gt; and &lt;code&gt;resolvers.js&lt;/code&gt;. In some real-world examples, you will see the schema and resolvers in the same file as your server. For this example, I decided to extract them to make them easier to read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;src/schema.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first type in our schema will be &lt;code&gt;Book&lt;/code&gt; which will represent a single book.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A schema is a collection of type definitions (hence "typeDefs") that together define the "shape" of queries that are executed against your data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our Book type looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # This "Book" type defines the queryable fields for every book in our data source.
  type Book {
    title: String
    author: String
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This created a queryable field called &lt;code&gt;Book&lt;/code&gt; which has a title and an author which are both strings. Now we add this &lt;code&gt;Book&lt;/code&gt; type to the &lt;code&gt;Query&lt;/code&gt; type. This type is used to identify all of the available queries that GraphQL clients can execute.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;typeDefs&lt;/code&gt; you will see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # The "Query" type is special: it lists all of the available queries that
  # clients can execute, along with the return type for each. In this
  # case, the "books" query returns an array of zero or more Books (defined above).
  type Query {
    books: [Book]
    book(title: String!): Book,
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added a &lt;code&gt;books&lt;/code&gt; query which returns an array of zero or more Books. We also added a single book query that takes a title and returns a single Book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FYI&lt;/strong&gt;:&lt;br&gt;
The &lt;code&gt;!&lt;/code&gt; in our type means the type is &lt;code&gt;non-nullable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have data for our books in &lt;code&gt;src/data/books.js&lt;/code&gt; but Apollo Server doesn't know anything about that data. We need to tell Apollo Server whenever it's executing a query for books to use that data. This is the role of resolvers. Let's look at our resolver for books. We can also add a resolver for a single book by searching for its title.&lt;/p&gt;

&lt;p&gt;Our resolver looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const resolvers = {
  Query: {
    books: () =&amp;gt; books,
    book: (parent, { title }) =&amp;gt; books.find((book) =&amp;gt; title === book.title)
  },
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the server
&lt;/h2&gt;

&lt;p&gt;In our &lt;code&gt;src/lambda/graphql.js&lt;/code&gt; you will see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({ typeDefs, resolvers });

exports.handler = server.createHandler();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are creating an instance of an apollo server and passing it our schema definition and our set of resolvers. Finally, pay close attention to the last line. This creates an export named &lt;code&gt;handler&lt;/code&gt; with a Lambda function handler. This is used for executing as our serverless function. Netlify functions are built with AWS Lambda under the hood.&lt;/p&gt;

&lt;p&gt;In your terminal, we can now run our GraphQL server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm start

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm start&lt;/code&gt; will run &lt;code&gt;NODE_ENV=development npm run start:lambda&lt;/code&gt; from our package.json scripts.&lt;/p&gt;

&lt;p&gt;As per apollo-server &lt;a href="https://www.apollographql.com/docs/apollo-server/deployment/lambda/"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The GraphQL Playground will only run if your NODE_ENV is set to development. If you don't pass this, or your NODE_ENV is set to production, you will not see the GraphQL Playground.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should see &lt;code&gt;Lambda server is listening on 9000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;localhost:9000/graphql&lt;/code&gt; to see our GraphQL playground.&lt;/p&gt;

&lt;p&gt;Type the following query into the playground to get back some books.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Write your query or mutation here
query {
  books {
    title
    author
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should give you back&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "books": [
      {
        "title": "The Awakening",
        "author": "Kate Chopin"
      },
      {
        "title": "City of Glass",
        "author": "Paul Auster"
      }
    ]
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the screenshot below of the playground to compare with yours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Mm81E3E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613156277280/NSlSkMXhF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Mm81E3E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613156277280/NSlSkMXhF.png" alt="Screenshot 2021-02-12 at 18.57.18.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also query for a specific book. Let's search for a book by its title.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Write your query or mutation here
query {
  book(title: "City of Glass") {
    title
    author
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return the following data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "book": {
      "title": "City of Glass",
      "author": "Paul Auster"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting countries data
&lt;/h2&gt;

&lt;p&gt;We will be getting the country's data from this GraphQL &lt;a href="//countries.trevorblades.com"&gt;API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;countries.js&lt;/code&gt; and place it in the &lt;code&gt;data&lt;/code&gt; directory.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;touch src/data/countries.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to install the &lt;code&gt;isomorphic-fetch&lt;/code&gt; package so we can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;fetch API&lt;/a&gt; to query the countries API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install isomorphic-fetch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, let's create a function that will query the countries API for every country including their name, country code, and capital. The query will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    query {
      countries {
        name
        code
        capital
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following code into &lt;code&gt;src/data/countries.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import fetch from 'isomorphic-fetch;

export const getCountries = async () =&amp;gt; {
  const url = 'https://countries.trevorblades.com/';

  const query = `
    query {
      countries {
        name
        code
        capital
      }
    }
  `;

  const opts = {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query }),
  };

  const response = await fetch(url, opts);
  return response.json();
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;POST&lt;/code&gt; request to the countries API for the information we need. We take the response which returns a promise. &lt;/p&gt;

&lt;p&gt;Why do we return &lt;code&gt;response.json()&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;We need to parse the response as JSON. You can read more about it on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Body/json"&gt;MDN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One thing you may have noticed is that this query will only work to get all countries.&lt;/p&gt;

&lt;p&gt;What if we want a single country? Let's refactor this function to filter by a country code if it is supplied.&lt;/p&gt;

&lt;p&gt;Change the query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const getCountries = async (code) =&amp;gt; {
  const url = 'https://countries.trevorblades.com/';
  let query;
  if (code) {
    query = `
      query {
        countries(filter: { code: { eq: "${code}" } }) {
          name
          code
          capital
        }
      }
    `
  } else { 
    query = `query {
      countries {
        name
        code
        capital
      }
    }`; 
  }

// rest of the code here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating country types and resolvers
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;src/schema.js&lt;/code&gt; let us add our Country type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This "Country" type defines the queryable fields for every country from the countries.trevorblades.com/ api
  type Country {
    code: String
    name: String
    capital: String
  }

  # The "Query" type is special: it lists all of the available queries that
  # clients can execute, along with the return type for each. In this
  # case, the "books" query returns an array of zero or more Books (defined above).
  type Query {
    books: [Book],
    book(title: String!): Book,
    countries(code: String): [Country]
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;src/resolvers.js&lt;/code&gt; let's add our resolver for countries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    countries: async (parent, { code }) =&amp;gt; {
      const { data: { countries } } = await getCountries(code);
      return [...countries.slice(0, 10)];
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created an asynchronous function that takes the country code. We await the response of the &lt;code&gt;getCountries&lt;/code&gt; function from &lt;code&gt;src/data/countries.js&lt;/code&gt; which is used to query the countries API. If the country code does not exist, it will query all of the countries. For this example, we return just the first 10 results. &lt;/p&gt;

&lt;h3&gt;
  
  
  Querying our data
&lt;/h3&gt;

&lt;p&gt;Let's restart our node server by running &lt;code&gt;npm start&lt;/code&gt; and go back to our playground on port 9000.&lt;/p&gt;

&lt;p&gt;Copy in the following query to search for all of the countries available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  countries {
    code
    name
    capital
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should return 10 countries from our resolver. &lt;/p&gt;

&lt;p&gt;Let's search for &lt;code&gt;United Arab Emirates&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  countries(code: "AE") {
    code
    name
    capital
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following country returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "countries": [
      {
        "code": "AE",
        "name": "United Arab Emirates",
        "capital": "Abu Dhabi"
      }
    ]
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Querying the Rick and Morty GraphQL API
&lt;/h2&gt;

&lt;p&gt;We could use the same approach with &lt;code&gt;isomorphic-fetch&lt;/code&gt;. However, I am going to show you another way to query this API.  This example shows how to query a third-party GraphQL &lt;a href="https://rickandmortyapi.com/graphq"&gt;API&lt;/a&gt; with the request function from Prisma's &lt;code&gt;graphql-request&lt;/code&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import { request } from 'graphql-request';

export const getCharacters = async () =&amp;gt; {
  const url = 'https://rickandmortyapi.com/graphql';
  const query = `
    query {
        characters {
            results {
                name
                id
            }
        }
    }
  `;

  const response = await request(url, query);
  return response;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding our character type and resolver
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;src/schema.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  # This "Character" type defines the queryable fields for every character from the Rick and Morty API
  type Character {
    name: String
    id: String
  }

  # The "Query" type is special: it lists all of the available queries that
  # clients can execute, along with the return type for each. In this
  # case, the "books" query returns an array of zero or more Books (defined above).

  type Query {
    books: [Book],
    book(title: String!): Book,
    countries(code: String): [Country],
    characters: [Character]
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;QUIZ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Test your skills now by querying for an individual character following similar steps from the countries example.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;src/resolvers.js&lt;/code&gt;, let's add our resolver for characters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   characters: async () =&amp;gt; {
      const { characters: { results } } = await getCharacters();
      return results;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Querying for a Rick and Morty character
&lt;/h2&gt;

&lt;p&gt;Open up your GraphQL playground on &lt;code&gt;localhost:9000/graphql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Paste in the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  characters {
    name
    id
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return an array of characters from the Rick and Morty GraphQL API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting our OMDB movies
&lt;/h2&gt;

&lt;p&gt;This OMBD API is a REST API. We could use &lt;code&gt;isomorphic-fetch&lt;/code&gt; or another library like &lt;code&gt;axios&lt;/code&gt;.&lt;br&gt;
Instead, we are going to use the &lt;code&gt;apollo-datasource-rest&lt;/code&gt; library. This was recommended by &lt;a href="https://moonhighway.com/apollo-datasources"&gt;Eve Porcello&lt;/a&gt; on &lt;code&gt;moonhighway.com&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To help developers make use of data from REST APIs, Apollo created REST Data Sources as a way to encapsulate data fetching. You might choose this utility over fetch because the package will help you with caching, deduplication, and error handling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To get started with this, create the following file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;touch src/data/omdb.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Paste in the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
  This example shows how to query the OMDB API using the apollo data source package
*/

import { RESTDataSource } from 'apollo-datasource-rest';

export class MoviesAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'http://www.omdbapi.com';
  }

  async getMovie(name) {
    const response = await this.get(`/?${name ? (`t=${name}`) : 'i=tt3896198'}&amp;amp;apikey=${process.env.OMDB_API_KEY}`);
    return response;
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This essentially just created a MoviesAPI class that has an async method &lt;code&gt;getMovie&lt;/code&gt; that will query the API and will change the query parameters if it's searching for a specific movie.  If we don't supply a name we will get a random movie.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; that I have my OMDB API store to my &lt;code&gt;.env&lt;/code&gt; file.  You will need to sign up to get a free API key. Read this &lt;a href="https://docs.netlify.com/configure-builds/environment-variables"&gt;post&lt;/a&gt; to understand more about using environment variables. These will serve useful as we deploy to Netlify and keep our API keys safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding our type and resolver for movie
&lt;/h3&gt;

&lt;p&gt;Our &lt;code&gt;src/schema.js&lt;/code&gt; should now look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* eslint-disable import/prefer-default-export */
import { gql } from 'apollo-server-lambda';

// # A schema is a collection of type definitions (hence "typeDefs")
// # that together define the "shape" of queries that are executed against
// # your data.

export const typeDefs = gql`
  # Comments in GraphQL strings (such as this one) start with the hash (#) symbol.

  # This "Book" type defines the queryable fields for every book in our data source.
  type Book {
    title: String
    author: String
  }

  # This "Country" type defines the queryable fields for every country from the countries.trevorblades.com/ API
  type Country {
    code: String
    name: String
    capital: String
  }

  # This "Character" type defines the queryable fields for every character from the rick and morty api
  type Character {
    name: String
    id: String
  }

  # This "Movie" type defines the queryable fields for a given movie from the OMDB API
  type Movie {
    Title: String
    Year: String
    Rated: String
  }

  # The "Query" type is special: it lists all of the available queries that
  # clients can execute, along with the return type for each. In this
  # case, the "books" query returns an array of zero or more Books (defined above).
  type Query {
    books: [Book],
    book(title: String!): Book,
    countries(code: String): [Country],
    characters: [Character],
    movie(name: String): Movie,
  }
`;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;src/resolvers.js&lt;/code&gt; should now look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Resolvers define the technique for fetching the types defined in the
// schema. This resolver retrieves books from the "books" array above.

import { getCountries } from './data/countries.js';
import { getCharacters } from './data/rickandmorty.js';
import books from './data/books.js';

export const resolvers = {
  Query: {
    books: () =&amp;gt; books,
    book: (parent, { title }) =&amp;gt; books.find((book) =&amp;gt; title === book.title),
    countries: async (parent, { code }) =&amp;gt; {
      const { data: { countries } } = await getCountries(code);
      return [...countries.slice(0, 10)];
    },
    characters: async () =&amp;gt; {
      const { characters: { results } } = await getCharacters();
      return results;
    },
    movie: (parent, { name }, { dataSources }) =&amp;gt; (name
      ? dataSources.movieAPI.getMovie(name)
      : dataSources.movieAPI.getMovie()
    ),
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How did we grab the movie name and our data sources from our resolver?&lt;br&gt;
Eve really made this clearer for us in her article &lt;a href="https://moonhighway.com/apollo-datasources"&gt;here&lt;/a&gt;.&lt;br&gt;
I really suggest giving it a read.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every resolver function takes in positional arguments. If there's anything that the resolver needs to execute appropriately (database details, authentication information, etc.), it will be passed via the third argument: context. An important thing to note is that Apollo Server puts dataSources on the context object so it can be used by the resolver functions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We need to supply this to our Apollo Server instance.&lt;br&gt;
In &lt;code&gt;src/lambda/graphql.js&lt;/code&gt; add in the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { MoviesAPI } from '../data/omdb.js';

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources: () =&amp;gt; ({
    movieAPI: new MoviesAPI(),
  })
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Querying for a movie
&lt;/h3&gt;

&lt;p&gt;Open the playground on &lt;code&gt;localhost:9000/graphql&lt;/code&gt;.&lt;br&gt;
Add the following query to search for a &lt;code&gt;Batman&lt;/code&gt; movie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
movie(name: "Batman") {
  Title
  Year
  Rated
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return the following data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "movie": {
      "Title": "Batman",
      "Year": "1989",
      "Rated": "PG-13"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, before we push to Github, we need to enable the GraphQL playground and &lt;a href="https://graphql.org/learn/introspection"&gt;introspection&lt;/a&gt; in production to see and play with it. Both of these are disabled as a production best-practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Netlify
&lt;/h2&gt;

&lt;p&gt;Now that we are ready to deploy our server. We can go ahead and commit our project to Github.&lt;br&gt;
If you need to figure out how to create a repository on Github you can follow these &lt;a href="https://docs.github.com/en/github/getting-started-with-github/create-a-repo"&gt;docs&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add -A
git commit -m "created graphql content hub"
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can grab all of the code for this project on my Github &lt;a href="https://github.com/garyb1/GraphQL-Content-Hub"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next thing we need to do is tell Netlify to deploy our project. Head on over to &lt;a href="//www.netlify.com"&gt;Netlify&lt;/a&gt; and create a free account to get started.&lt;/p&gt;

&lt;p&gt;You will see something like this when you are logged in:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V9Gh-Qkv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613223799756/7RxVPbtTT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9Gh-Qkv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613223799756/7RxVPbtTT.png" alt="Screenshot 2021-02-13 at 13.42.48.png" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, click &lt;code&gt;new site from git&lt;/code&gt; and follow the steps.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;netlify.toml&lt;/code&gt; file will be used here during the build phase. This will run the &lt;code&gt;npm run build&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You will need to go to &lt;code&gt;Build and Deploy&lt;/code&gt; and in environments set your &lt;code&gt;OMDB_API_KEY&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Jd66-hm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613225109834/M4eN5Canx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Jd66-hm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613225109834/M4eN5Canx.png" alt="Screenshot 2021-02-13 at 14.03.45.png" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If if the build fails as a result of your missing environment variable, you just need to manually trigger a deployment in the deploys section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vGzZsHY0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613226059109/w6xwimcfT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vGzZsHY0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613226059109/w6xwimcfT.png" alt="Screenshot 2021-02-13 at 14.20.01.png" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run into issues with &lt;code&gt;encoding&lt;/code&gt; when running the build, trying installing encoding with&lt;br&gt;
&lt;code&gt;npm install encoding&lt;/code&gt;. You can read about the issue &lt;a href="https://community.netlify.com/t/error-could-not-find-encoding-module-in-file-netlify-function/2259/18"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If all goes smoothly, you should see the following message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IMOLAmGb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613226177925/CmeTm3e3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IMOLAmGb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1613226177925/CmeTm3e3c.png" alt="Screenshot 2021-02-13 at 14.21.37.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My site has now deployed to &lt;code&gt;gql-content-hub.netlify.app/.netlify/functions/graphql&lt;/code&gt;.&lt;br&gt;
Feel free to visit the &lt;a href="https://gql-content-hub.netlify.app/.netlify/functions/graphql"&gt;playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you don't like the endpoint being "&lt;em&gt;/.netlify/functions&lt;/em&gt;"&lt;br&gt;
you can change it by adding a redirect in &lt;code&gt;netlify.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tutorial complete
&lt;/h2&gt;

&lt;p&gt;As always I hope you enjoyed this tutorial. If you have any feedback please do let me know and I will take it on board. If you liked it and would like to see more posts from me then please do follow me. You can also reach me on &lt;a href="https://twitter.com/garybyrne1995"&gt;Twitter&lt;/a&gt;. You can grab all of the code for this project on my Github &lt;a href="https://github.com/garyb1/GraphQL-Content-Hub"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://moonhighway.com/fetching-data-from-a-graphql-api"&gt;Eve Porcello - Fetching Data from a GraphQL API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apollographql.com/docs"&gt;Apollo GraphQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphql</category>
      <category>netlify</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fizzbuzz with CSS and Pug</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Wed, 03 Feb 2021 14:52:27 +0000</pubDate>
      <link>https://dev.to/garybyrne/fizzbuzz-with-css-and-pug-4gkg</link>
      <guid>https://dev.to/garybyrne/fizzbuzz-with-css-and-pug-4gkg</guid>
      <description>&lt;p&gt;Back in 2017, I entered a frontend interview and I was asked to write some code on a whiteboard. I was given the &lt;a href="https://www.geeksforgeeks.org/fizz-buzz-implementation/" rel="noopener noreferrer"&gt;Fizzbuzz&lt;/a&gt; problem. Without thinking I immediately went for JavaScript. Recently I learned about CSS counters and that got me thinking. What if we can do it with CSS? &lt;/p&gt;

&lt;p&gt;In this article, I am going to show you two easy ways to solve the Fizzbuzz problem with CSS. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Fizzbuzz question?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Write a program that prints the numbers from 1 to 100.&lt;br&gt;
For multiples of &lt;code&gt;3&lt;/code&gt; print &lt;code&gt;Fizz&lt;/code&gt; instead of the number,&lt;br&gt;
for the multiples of &lt;code&gt;5&lt;/code&gt; print &lt;code&gt;Buzz&lt;/code&gt; and for &lt;br&gt;
multiples of both &lt;code&gt;5 and 3&lt;/code&gt; print &lt;code&gt;FizzBuzz&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solution 1: Using CSS Counters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  We can loop in &lt;a href="https://pugjs.org/api/getting-started.html" rel="noopener noreferrer"&gt;Pug&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Loop for 1 to 100 and create an HTML element. In this example, we are creating a div. We will use each div to hold the counter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- const range = 100;
- for(let i = 1; i &amp;lt;= range; i++)
    .fizz

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using a for loop here. We could also write an &lt;a href="https://pugjs.org/language/iteration.html" rel="noopener noreferrer"&gt;each&lt;/a&gt; &lt;br&gt;
iteration such as &lt;code&gt;each _ in Array(100)&lt;/code&gt; instead.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using CSS counters
&lt;/h3&gt;

&lt;p&gt;To get started, we must first create a &lt;code&gt;counter-reset&lt;/code&gt;.  This will initialize our CSS counter, giving it a name and setting its value. By default, the value is 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
    counter-reset: fizz;
    // counter initialized with value of 0, same as saying "counter-reset: fizz 0;"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a counter has been created, we can use &lt;code&gt;counter-increment&lt;/code&gt; to increment its value.&lt;br&gt;
For every &lt;code&gt;fizz&lt;/code&gt; div, let's update the counter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.fizz {
    counter-increment: fizz;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will set the content for our divs. We can utilize CSS &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements" rel="noopener noreferrer"&gt;pseudo-elements&lt;/a&gt; to set the content of the div.&lt;/p&gt;

&lt;p&gt;If the div is a multiple of 3, set its &lt;code&gt;::before&lt;/code&gt; content to 'Fizz '. If the div is a multiple of 5, set is &lt;code&gt;::after&lt;/code&gt; content to 'Buzz'. If a div is a multiple of both 3 and 5, the before and after pseudo-elements will combine to form &lt;code&gt;Fizz Buzz&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.fizz:nth-child(3n)::before {
  content: 'fizz ';
}

.fizz:nth-child(5n)::after {
  content: 'buzz';  
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we want to set the counter value for those other divs that are not a multiple of 3 or 5. To set the &lt;code&gt;::before&lt;/code&gt; content of the div to use the counter value, we use &lt;code&gt;counter&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
.fizz::before {
    content: counter(fizz);
}

.fizz:nth-child(3n)::before {
  content: 'fizz ';
}

.fizz:nth-child(5n)::after {
  content: 'buzz';  
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the following: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1612354739834%2FhpFgTA0nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1612354739834%2FhpFgTA0nq.png" alt="Screenshot 2021-02-03 at 12.18.44.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note how the divs with a multiple of 5 and not a multiple of 3 still get the number applied. To fix this we can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:not" rel="noopener noreferrer"&gt;not&lt;/a&gt; pseudo-class. When the div is &lt;code&gt;not&lt;/code&gt; a multiple of 5, then set the before content to the counter number.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.fizz:not(:nth-child(5n))::before {
    content: counter(fizz);
}
.fizz:nth-child(3n)::before {
  content: 'fizz ';
}

.fizz:nth-child(5n)::after {
  content: 'buzz';  
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a working example in codepen:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/zYovrVB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactoring our Pug
&lt;/h3&gt;

&lt;p&gt;In some cases, an interviewer might ask to change your code to work for a different range instead of 1 to 100. We can write a Pug mixin to take a min and max value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mixin fizzbuzz(min, max)
    - for(let i = min; i &amp;lt;= max; i++)
        .fizz

+fizzbuzz(1, 100)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a working codepen example:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/RwoWRNz?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Support
&lt;/h3&gt;

&lt;p&gt;At the time of writing, CSS Counters are supported in most major browsers. See &lt;a href="https://caniuse.com/css-counters" rel="noopener noreferrer"&gt;Can I Use&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 2: Using an Ordered List
&lt;/h2&gt;

&lt;p&gt;Let's copy the same pug from the last example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mixin fizzbuzz(min, max)
    ol
        - for(let i = min; i &amp;lt;= max; i++)
            li

+fizzbuzz(1, 100)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we have an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol" rel="noopener noreferrer"&gt;ordered list&lt;/a&gt; here which returns a list of numbered elements. So if we render 100 list items in that ordered list, we have the numbers already available to view. Although it makes no sense semantically, it is just to show how we might do this. &lt;/p&gt;

&lt;p&gt;In this ordered list, we can set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-position" rel="noopener noreferrer"&gt;list-style-position&lt;/a&gt; of each list item to &lt;code&gt;inside&lt;/code&gt;. This will set the position of everything inside, relative to the list which created the nice alignment. We then just set the list style to none to remove the numbering on those items that need the text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//scss

li {
    list-style-position: inside;

    &amp;amp;:nth-child(3n), &amp;amp;:nth-child(5n){
         list-style-type: none;
    }

    &amp;amp;:nth-child(3n)::before {
          content: 'Fizz';  
    }

    &amp;amp;:nth-child(5n)::after {
          content: ' Buzz';  
    } 
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a working example: &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/LYbpRbG?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>pug</category>
      <category>html</category>
    </item>
    <item>
      <title>Creating a Xylophone using CSS and Pug</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Tue, 02 Feb 2021 16:38:25 +0000</pubDate>
      <link>https://dev.to/garybyrne/creating-a-xylophone-using-css-and-pug-4dl9</link>
      <guid>https://dev.to/garybyrne/creating-a-xylophone-using-css-and-pug-4dl9</guid>
      <description>&lt;p&gt;Let's create a simple xylophone with some CSS and Pug. The xylophone sounds I will be using in this blog can be found &lt;a href="https://freesound.org/people/DANMITCH3LL/sounds/"&gt;here&lt;/a&gt;. The resulting codepen can be found &lt;a href="https://codepen.io/garyb1/pen/f59c52a7c9d00dd1d230491908d1f0e5"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What will it look like?
&lt;/h2&gt;

&lt;p&gt;We will use an unordered list of buttons to represent the xylophone. The finished product will look like the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aCfLRyYz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612281745582/pqxy41fe_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aCfLRyYz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612281745582/pqxy41fe_.png" alt="Screenshot 2021-02-02 at 15.55.36.png" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Xylophone
&lt;/h2&gt;

&lt;p&gt;To get up and running quickly with &lt;a href="https://pugjs.org/api/getting-started.html"&gt;Pug&lt;/a&gt; you can open up a &lt;a href="https://codepen.io"&gt;codepen&lt;/a&gt;. In your HTML settings, click Pug as your HTML preprocessor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing our Pug
&lt;/h3&gt;

&lt;p&gt;Let's create an unordered list of buttons using an array of xylophone notes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- const notes = ['c', 'd', 'e', 'f', 'g', 'a', 'b', 'c2'];

main
  ul.xylophone(role="list")
    each note, index in notes
      li
        button #{note}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;main&amp;gt;
  &amp;lt;ul class="xylophone" role="list"&amp;gt;
    &amp;lt;li&amp;gt;
      &amp;lt;button&amp;gt;c&amp;lt;/button&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;
      &amp;lt;button&amp;gt;d&amp;lt;/button&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;
      &amp;lt;button&amp;gt;e&amp;lt;/button&amp;gt;
    &amp;lt;/li&amp;gt;
    // ..... the rest
  &amp;lt;/ul&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added &lt;code&gt;role="list"&lt;/code&gt; to the &lt;code&gt;ul&lt;/code&gt; to overcome a semantics &lt;a href="https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html"&gt;issue&lt;/a&gt; in voiceover and safari. &lt;/p&gt;

&lt;h3&gt;
  
  
  Let's style our xylophone with CSS.
&lt;/h3&gt;

&lt;p&gt;First, let's reset &lt;code&gt;box-sizing&lt;/code&gt; and position the content to the center of the page.&lt;/p&gt;

&lt;p&gt;Alternatively, you can just import a CSS reset. I recommend the &lt;a href="https://piccalil.li/blog/a-modern-css-reset"&gt;modern CSS reset&lt;/a&gt; by &lt;a href="https://piccalil.li"&gt;Andy Bell&lt;/a&gt; but it's not necessary for this project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*,
*:before,
*:after {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  margin: 0;
  display: grid;
  place-items: center;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can style our &lt;code&gt;ul&lt;/code&gt; to be a &lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;flex&lt;/a&gt; container. Using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors"&gt;attribute selector&lt;/a&gt; here just to open our CSS to other types of lists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[role="list"] {
  list-style: none;
  display: flex;
  justify-content: space-between;
  padding: 0;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jB-tZKsY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612276157190/XUVu98SXr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jB-tZKsY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612276157190/XUVu98SXr.png" alt="Screenshot 2021-02-02 at 14.29.02.png" width="538" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can add some responsive sizing to our xylophone.&lt;br&gt;
We will apply the &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units"&gt;vmin&lt;/a&gt; relative length unit using CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;custom properties&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  --desired-size: 60;
  --coefficient: 1vmin;
  --size: calc(var(--desired-size) * var(--coefficient));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's update our list with the new sizing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[role="list"] {
  list-style: none;
  display: flex;
  justify-content: space-between;
  padding: 0;
  height: calc(1.5 * var(--size));
  width: calc(2.5 * var(--size));
}

li {
  width: 10%;
}

button {
  width: 100%;
  height: 100%;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's apply the backboards of the xylophone. We will be &lt;code&gt;absolutely&lt;/code&gt; positioning these against our xylophone. To do this, we must first set &lt;code&gt;position: relative;&lt;/code&gt; in our &lt;code&gt;[role="list"]&lt;/code&gt; CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.xylophone:before,
.xylophone:after {
  content: "";
  position: absolute;
  z-index: -1; // hide these to the back, allow our buttons to appear in front
  background: black;
  height: 15%; // 15% of the overall xylophone height
  width: 100%;
}

.xylophone:before {
  top: 10%;
  transform: rotate(3deg);
}

.xylophone:after {
  bottom: 10%;
  transform: rotate(-3deg);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5o22LY93--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612277217481/iqd-cWp34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5o22LY93--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612277217481/iqd-cWp34.png" alt="Screenshot 2021-02-02 at 14.46.36.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooking up our audio
&lt;/h3&gt;

&lt;p&gt;Before we continue to style our xylophone, let's add some audio to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main
  ul.xylophone(role="list")
    each note, index in notes
      li
        button(onclick=`playNote('${note}')`)
          audio(
            data-key=`${note}`,
            src=`https://s3-us-west-2.amazonaws.com/s.cdpn.io/1312918/${note}.wav`
          )

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added a hidden audio element to be a child of the button. We can hook into this to play each note sound. I have added a &lt;code&gt;src&lt;/code&gt; attribute to point to the different &lt;code&gt;wav&lt;/code&gt; files. The &lt;code&gt;data-key&lt;/code&gt; attribute will be used within our querySelector to help us find an audio element for each individual note. In this example, I have stored them on my codepen s3 bucket. Next, I will need to add some JavaScript to handle the &lt;code&gt;on click&lt;/code&gt; logic.&lt;/p&gt;

&lt;p&gt;At the bottom of your &lt;code&gt;pug&lt;/code&gt; file, add the following script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script.
  function playNote(note) {
    const audioElement = document.querySelector(`audio[data-key="${note}"]`);
    audioElement.currentTime = 0;
    audioElement.play();
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleaning up our xylophone
&lt;/h3&gt;

&lt;p&gt;Let's add some color to our buttons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;li:nth-child(1) button {
  background-color: pink;
}
li:nth-child(2) button  {
  background-color: orange;
}
li:nth-child(3) button {
  background-color: yellow;
}
li:nth-child(4) button {
  background-color: lightgreen;
}
li:nth-child(5) button {
  background-color: green;
}
li:nth-child(6) button{
  background-color: skyblue;
}
li:nth-child(7) button{
  background-color: blue;
}
li:nth-child(8) button{
  background-color: rebeccapurple;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tgzFNzqb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612278046628/QUX2QpIlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tgzFNzqb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1612278046628/QUX2QpIlx.png" alt="Screenshot 2021-02-02 at 15.00.31.png" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's add the knobs for each button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;button {
  width: 100%;
  height: 100%;
  position: relative;
  border-radius: 5px;
}

button::before,
button::after {
  content: '';
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  height: 5%;
  width: 35%;
  border-radius: 50%;
  background-color: white;
  box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
}

button::before {
  top: 5%;
}

button::after {
  bottom: 5%;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a working xylophone. Here is a working version:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/eYBNyQp?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning up our Xylophone
&lt;/h2&gt;

&lt;p&gt;There is a number of things we can do to tidy up our component.&lt;/p&gt;

&lt;p&gt;When we click a button, we can apply a class to show the sound is playing.&lt;br&gt;
For the same button, we can also add an event listener to remove the class&lt;br&gt;
when the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/transitionend_event"&gt;transitionend&lt;/a&gt; event is fired. &lt;br&gt;
For this, we will remove the class when the &lt;code&gt;box-shadow&lt;/code&gt; transition has ended.&lt;/p&gt;

&lt;p&gt;Let's add a transition to our button and a nice &lt;code&gt;box-shadow&lt;/code&gt; when the button is playing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;button {
  //..other styles
  transition: all 1s ease; //we can listen for the box shadow end
}

button.playing {
  border-color: #ffc600;
  box-shadow: 0px -10px 1rem #FFC600;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;data-key&lt;/code&gt; attribute with the value of the note to our button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   button(onclick=`playNote('${note}')`, data-key=`${note}`)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then apply the &lt;code&gt;.playing&lt;/code&gt; class when we click the button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script.
  function playNote(note) {
    const audioElement = document.querySelector(`audio[data-key="${note}"]`);
    const buttonElement = document.querySelector(`button[data-key="${note}"]`);
    buttonElement.classList.add('playing');   
    audioElement.currentTime = 0;
    audioElement.play();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add our &lt;code&gt;transitionend&lt;/code&gt; event listener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script.
  function removeStyles(e) {
    if (e.propertyName !== 'box-shadow') return;
    e.target.classList.remove('playing');
  }

  function playNote(note) {
    const audioElement = document.querySelector(`audio[data-key="${note}"]`);
    const buttonElement = document.querySelector(`button[data-key="${note}"]`);
    buttonElement.classList.add('playing');
    buttonElement.addEventListener('transitionend', removeStyles);
    audioElement.currentTime = 0;
    audioElement.play();
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a nice transition on our xylophone:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/VwmLxgr?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We can do a lot more with Pug. I created another example to show how we can pass values from pug into our CSS to use as custom properties.&lt;/p&gt;

&lt;p&gt;I randomly generate the hue for our background color each time, and I can pass the index which I use to make each button smaller in height and create a nice horizontal rhythm. In the pen below, you can also see how I used the &lt;code&gt;kbd&lt;/code&gt; element instead of the &lt;code&gt;button&lt;/code&gt; element to listen for keyboard events.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/XWmLXgd?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.twitch.tv/jh3yy/about"&gt;Jhey Tompkins Twitch&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://codepen.io/jh3y"&gt;Jhey Tompkins Codepen&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://codepen.io/garyb1"&gt;My Codepen&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Font Loading with FontFace Observer - Getting Started</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Tue, 02 Feb 2021 09:51:53 +0000</pubDate>
      <link>https://dev.to/garybyrne/font-loading-with-fontface-observer-getting-started-3lgo</link>
      <guid>https://dev.to/garybyrne/font-loading-with-fontface-observer-getting-started-3lgo</guid>
      <description>&lt;h1&gt;
  
  
  Font Loading with Font Face Ob­server
&lt;/h1&gt;

&lt;h2&gt;
  
  
  What is Font Face Observer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://fontfaceobserver.com/"&gt;Font Face Observer&lt;/a&gt; is a really great web font loader created by &lt;a href="https://www.bramstein.com/"&gt;Bram Stein&lt;/a&gt; that provides us with a promised based way to control our font loading. It will know when web fonts have been loaded which gives us complete control to customize the font loading experience as we want.&lt;/p&gt;

&lt;p&gt;With web fonts, we decide if we want to self-host or load from external services, so it can be difficult to control our browser's font loading behavior. We need to be careful with problems in our loading like &lt;a href="https://www.zachleat.com/web/webfont-glossary/#foit"&gt;FOIT&lt;/a&gt; or &lt;a href="https://www.zachleat.com/web/webfont-glossary/#fout"&gt;FOUT&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Font-Display Swap
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@font-face {
  font-family: "Font Family";
  src: url('....url.to.font') format('format');
  font-display: swap;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the MDN  &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display"&gt;Docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the font face is not loaded, any element attempting to use it must render a fallback font face. If the font face successfully loads during this period, it is used normally. This introduces &lt;a href="https://www.zachleat.com/web/webfont-glossary/#fout"&gt;FOUT&lt;/a&gt; which may cause a significant layout shift.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to have greater flexibility with our fallback font. With FOUT, it gives us that fallback font but doesn't give us a way to tweak the harsh viewing when layout shift occurs. By using Font Face Observer, it can provide us with a way of controlling this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation of Font Face Observer
&lt;/h2&gt;

&lt;p&gt;Using &lt;a href="https://www.npmjs.com/package/fontfaceobserver"&gt;npm&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   npm install fontfaceobserver -S
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using yarn&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   yarn add fontfaceobserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are not using &lt;a href="https://nodejs.org/en/"&gt;node&lt;/a&gt;, you can add it by linking the script file into the head of the document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// locally
&amp;lt;script src="js/vendor/fontfaceobserver.js"&amp;gt;&amp;lt;/script&amp;gt;
//or from CDN
&amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fontfaceobserver/2.1.0/fontfaceobserver.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Roboto Example
&lt;/h2&gt;

&lt;p&gt;Let's grab the Roboto 'Regular', 'Medium' and 'Bold' from &lt;a href="https://fonts.google.com/specimen/Roboto?sidebar.open=true&amp;amp;selection.family=Roboto:wght@300;400;700"&gt;Google Fonts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, let's load our fonts in our CSS and point to their directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@font-face {
  font-family: "Roboto";
  font-weight: 400;
  src: url("../fonts/Roboto-Regular.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "Roboto";
  font-weight: 500;
  src: url("../fonts/Roboto-Medium.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "Roboto";
  font-weight: 700;
  src: url("../fonts/Roboto-Bold.ttf") format("truetype");
  font-display: swap;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can start using FontFace Observer.&lt;br&gt;
Create a script tag in the head of our document below where we brought in FontFace Observer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.documentElement.className += " roboto-inactive";
const RobotoFont = new FontFaceObserver("Roboto", {});

RobotoFont.load().then(function () {
    document.documentElement.classList.remove("roboto-inactive");
    document.documentElement.classList.add("roboto-active");
    sessionStorage.foutFontsLoaded = true;
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we are doing here is appending some classes to the root of our document whenever our RobotoFont promise resolves. The promise will resolve when the font has loaded. We can use the &lt;code&gt;roboto-inactive&lt;/code&gt; class in our CSS to style our fallback font however we want. This class will only be present when the font fails to load. &lt;/p&gt;

&lt;p&gt;If we wanted to load multiple fonts we use &lt;code&gt;Promise.all&lt;/code&gt; which will make sure we wait for both promises to resolve before executing our important code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.documentElement.className += " wf-inactive";
const robotoFont = new FontFaceObserver("Roboto", {});
const poppinsFont = new FontFaceObserver("PoppinsFont", {
       weight: 700, // we can be more precise
       style: italic
});

Promise.all([robotoFont.load(), poppinsFont.load()]).then(function () {
   // Important code here.... add a class or remove, etc.
});

// We can also provide a second function to 
// run when the font is not available

Promise.all([robotoFont.load(), poppinsFont.load()]).then(
function () {
   console.log('font is available');
   // Important code here.... add a class or remove, etc.
},
function () {
  console.log('font is not available');
 // do something here ...
});


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our CSS, we can now add some styles to clean up our fallback font or add existing styles to our loaded font.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
body {
    font-family: 'Roboto', Arial, Helvetica, sans-serif;
}

.wf-inactive body {
    font-family: Arial, Helvetica, sans-serif;
}


.wf-inactive h1,
.wf-inactive h2,
.wf-inactive h3 {
   // you could also apply the font-family to specific
  // elements if we had a heading font for example.
}

.wf-inactive p { 
    // apply these styles to a pargraph using our fallback font 
    line-height: 1.2;
    letter-spacing: -0.5px;
}

// ... more styles here

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Support
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Promise Support
&lt;/h3&gt;

&lt;p&gt;In the FontFace Observer &lt;a href="https://www.npmjs.com/package/fontfaceobserver"&gt;README&lt;/a&gt; it says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FontFace Observer uses Promises in its API, so for browsers that do not support promises, you'll need to include a polyfill. If you use your own Promise polyfill you just need to include fontfaceobserver.standalone.js in your project. If you do not have an existing Promise polyfill you should use fontfaceobserver.js which includes a small Promise polyfill. Using the Promise polyfill adds roughly 1.4KB (500 bytes gzipped) to the file size.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Browser Support
&lt;/h3&gt;

&lt;p&gt;You can see the browser support within the package &lt;a href="https://www.npmjs.com/package/fontfaceobserver#browser-support"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.npmjs.com/package/fontfaceobserver"&gt;FontFace Observer NPM&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/bramstein/fontfaceobserver"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://fontfaceobserver.com/"&gt;FontFace Observer Website&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://frontendmasters.com/courses/responsive-typography-v2"&gt;Jason Pamental Frontend Masters Course&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.bramstein.com/writing/web-font-loading-patterns.html"&gt;Bram Stein&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Variable Fonts in Your Web Projects - Getting Started</title>
      <dc:creator>Gary Byrne</dc:creator>
      <pubDate>Tue, 02 Feb 2021 09:47:38 +0000</pubDate>
      <link>https://dev.to/garybyrne/variable-fonts-in-your-web-projects-getting-started-55e0</link>
      <guid>https://dev.to/garybyrne/variable-fonts-in-your-web-projects-getting-started-55e0</guid>
      <description>&lt;p&gt;Before we check out some code, let us briefly discuss what variable fonts are.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenType Font Variations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview"&gt;OpenType font variations&lt;/a&gt; allow us to create a single font file that contains a variety of fonts from a single typeface. &lt;br&gt;
Variable fonts are fonts that use &lt;a href="https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview"&gt;OpenType&lt;/a&gt; font variations. By using variable fonts, we now have access to every variation of a given typeface in a single file, reducing the number of font files considerably. We no longer need a font file for every single style, weight, or even width.&lt;/p&gt;
&lt;h3&gt;
  
  
  Font Variation Settings and the Variation Axis
&lt;/h3&gt;

&lt;p&gt;With variable fonts, one of the main concepts is an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings"&gt;axis of variation&lt;/a&gt;. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings"&gt;axis of variation&lt;/a&gt; is a range of values to describe a specific aspect of a typeface.&lt;br&gt;
For example, a &lt;em&gt;'slant' axis * describes how much slant should exist in letterforms, or a *'wght' axis&lt;/em&gt; describes how much weight exists in letterforms.&lt;/p&gt;

&lt;p&gt;Typeface designers have the ability to use five registered axes of variations.&lt;/p&gt;

&lt;p&gt;These are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Weight&lt;/li&gt;
&lt;li&gt;Width&lt;/li&gt;
&lt;li&gt;Slant&lt;/li&gt;
&lt;li&gt;Italic&lt;/li&gt;
&lt;li&gt;Optical size. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These five axes were added to the &lt;a href="https://drafts.csswg.org/css-fonts-4"&gt;specification&lt;/a&gt;. &lt;br&gt;
Designers also have the ability to add custom axes for their variable font.&lt;/p&gt;
&lt;h3&gt;
  
  
  Loading a Variable Font.
&lt;/h3&gt;

&lt;p&gt;I &lt;a href="https://dev.toLink"&gt;wrote&lt;/a&gt; about loading a variable font that uses &lt;a href="https://github.com/bramstein/fontfaceobserver"&gt;FontFaceObserver&lt;/a&gt;. This can be used to optimize your font loading.&lt;/p&gt;
&lt;h4&gt;
  
  
  Specify a font using @ &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face"&gt;&lt;strong&gt;&lt;em&gt;font-face&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt; CSS rule
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@font-face {
    font-family: "Vollkorn"; // typeface we are using
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/1312918/Vollkorn-VariableFont_wght.ttf') 
    format('truetype-variations'); // we have a .ttf font file, support Safari, Android, iOS
    font-display: swap; //browser use fallback font to display content until custom font has downloaded
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Depending on the type of variable font you are using, you can set the format as &lt;code&gt;woff2-variations&lt;/code&gt;, &lt;code&gt;woff-variations&lt;/code&gt;, &lt;code&gt;opentype-variations&lt;/code&gt; or &lt;code&gt;truetype-variations&lt;/code&gt;. This &lt;a href="https://css-tricks.com/one-file-many-options-using-variable-fonts-web/"&gt;blog&lt;/a&gt; from Ollie Williams on CSS-Tricks explains it in more detail. As Ollie says, we should try our best to use woff2 where possible.&lt;/p&gt;
&lt;h4&gt;
  
  
  Use font-family and set &lt;strong&gt;font-variation-settings&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Let us change the the font-weight by using font-variation setttings. If we set the weight to 400,&lt;br&gt;
it will look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 {
    font-family: 'Vollkorn', serif;
    font-variation-settings: 'wght' 400; // see wakamaifondue to get min and max values
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ORCijcdB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611942677052/ydEiRrHdJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ORCijcdB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611942677052/ydEiRrHdJ.png" alt="Screenshot 2021-01-29 at 17.51.06.png" width="564" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if we decide to change it to the max weight, which is 900, we will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 {
    font-family: 'Vollkorn', serif;
    font-variation-settings: 'wght' 900; // see wakamaifondue to get min and max values
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jeEQ-yQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611942854007/iQkBoDFUN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jeEQ-yQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611942854007/iQkBoDFUN.png" alt="Screenshot 2021-01-29 at 17.54.04.png" width="642" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can perform animation by changing the font-variation-settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@keyframes scale {
    0% {
        font-variation-settings: 'wght' 400;
    }
    50% {
        font-variation-settings: 'wght' 900;
    }
    100% {
        font-variation-settings: 'wght' 400;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a fun animating variable font:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/qBOMmNV?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Support for Older Browsers
&lt;/h4&gt;

&lt;p&gt;Check &lt;a href="https://caniuse.com/?search=variable%20fonts"&gt;Can I Use&lt;/a&gt; for variable font support.&lt;/p&gt;

&lt;p&gt;You can check if the browser supports font variation settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@supports (font-variation-settings: normal) {
  html {
    font-family: 'SourceSansVariable', sans-serif;
    font-variation-settings: "wght" 900;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also check if the browser does not support font variation settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@supports not (font-variation-settings: normal) {
  html {
    font-family: sans-serif
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Finding Variation Axes - Wakamaifondue
&lt;/h3&gt;

&lt;p&gt;A service that I use is &lt;a href="https://wakamaifondue.com/"&gt;wakamaifondue&lt;/a&gt;, which provides us with tonnes of information about an uploaded font file.  When I uploaded a &lt;strong&gt;DecovarAlpha&lt;/strong&gt; variable font to &lt;a href="https://wakamaifondue.com/"&gt;wakamaifondue&lt;/a&gt;, I could see it provides me with a short summary about that font.  It also provides an interactive area where you can change the variations of axes and see the available characters for that font.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a TrueType variable font with 114 characters. It has 15 axes and 0 named instances. It has no layout features.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wrote a &lt;a href="https://twitter.com/garybyrne1995/status/1278632855699103745"&gt;tweet&lt;/a&gt; showing how useful this can be. You can see it below:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1278632855699103745-960" src="https://platform.twitter.com/embed/Tweet.html?id=1278632855699103745"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1278632855699103745-960');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1278632855699103745&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Variable Fonts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Single File&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As we are only loading one single variable font file which includes all variations of a typeface, it reduces the number of HTTP requests considerably. &lt;/p&gt;

&lt;p&gt;Let us pretend we are dealing with static fonts and we decided to use the &lt;a href="https://fonts.google.com/specimen/Poppins"&gt;Poppins typeface&lt;/a&gt;. If we needed every font variation of Poppins, we need to make a request for every file. If we need 'Poppins Regular', 'Poppins Bold', and 'Poppins Bold Italic', then we have three font files to deal with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Full Variation Access&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With variable fonts, we have access to the full variation settings for that typeface, meaning we can do more with our type and really make the most out of our designs. &lt;/p&gt;

&lt;p&gt;*&lt;em&gt;3. Fluid Type Animations *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We have the ability to animate between the different variations in our file, which means we have endless possibilities for animation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codepen Examples
&lt;/h3&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/garyb1/embed/LYpJVPO?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A codepen example by Gary Byrne showing how to animate a variable font.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/mandymichael/embed/dJZQaz?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A codepen example by Mandy Michael showing how to animate a variable font.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;p&gt;A list of useful links that really helped me understand variable fonts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview"&gt;OpenType Specification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://frontendmasters.com/courses/responsive-typography-v2/"&gt;Frontend Masters Course By Jason Pamental&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/mandymichael"&gt;Mandy Michael Codepen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide"&gt;MDN Variable Fonts Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://v-fonts.com/"&gt;Variable Fonts List&lt;/a&gt;&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>javascript</category>
      <category>css</category>
    </item>
  </channel>
</rss>
