<?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: Ben Robertson</title>
    <description>The latest articles on DEV Community by Ben Robertson (@benrobertson).</description>
    <link>https://dev.to/benrobertson</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%2F89377%2F44513565-9fdd-46d0-8d6f-1c3d720c179a.jpg</url>
      <title>DEV Community: Ben Robertson</title>
      <link>https://dev.to/benrobertson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/benrobertson"/>
    <language>en</language>
    <item>
      <title>The Sneaky Total Blocking Time Issue That PageSpeed Insights Didn’t Catch</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Fri, 22 Aug 2025 16:42:24 +0000</pubDate>
      <link>https://dev.to/benrobertson/the-sneaky-total-blocking-time-issue-that-pagespeed-insights-didnt-catch-522m</link>
      <guid>https://dev.to/benrobertson/the-sneaky-total-blocking-time-issue-that-pagespeed-insights-didnt-catch-522m</guid>
      <description>&lt;p&gt;For months, I was plagued by a performance issue on ColorBliss.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F516z3vvp9nhcqtrzja19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F516z3vvp9nhcqtrzja19.png" alt="A screenshot of a tweet wherein I show 107 seconds of total blocking time classified as " width="800" height="776"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PageSpeed Insights reported 100+ seconds (!!!!) of total blocking time on my home page and a couple of my key landing pages (like this &lt;a href="https://colorbliss.com/photo-to-coloring-page-converter" rel="noopener noreferrer"&gt;photo to coloring page&lt;/a&gt; converter).&lt;/p&gt;

&lt;p&gt;I was starting to lose my organic rankings to competitors, and I was convinced that the poor performance on my marketing pages was part of it.&lt;/p&gt;

&lt;p&gt;Having worked with a lot of companies on performance issues in the past, I knew that total blocking time (TBT) was one of the hardest Core Web Vitals to tackle. It usually involved getting deep into the weeds of the chrome dev tools performance tabs and untangling a mess of JavaScript code and dependencies to find slow downs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging into PageSpeed Insights
&lt;/h2&gt;

&lt;p&gt;Usually PageSpeed Insights gives you a helpful starting point, like Script Evaluation or Rendering. But not this time.&lt;/p&gt;

&lt;p&gt;This time, 100+ seconds were showing up under "Other":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7h3jyphre5wcpb1zjru.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7h3jyphre5wcpb1zjru.jpeg" alt="The breakdown of total blocking time by category (100s were classified as other)" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, that's not helpful at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging Deeper with DevTools
&lt;/h2&gt;

&lt;p&gt;So I did what I've always done in the past: I opened up the Chrome Dev Tools performance tab and started poking around at the on-page JavaScript. I removed packages, changed analytics implementations. But nothing worked.&lt;/p&gt;

&lt;p&gt;Finally, I had enough, and assuming that it was an issue in my underlying Next.js usage, I decided to do an MVP of a migration to Astro for my marketing pages.&lt;/p&gt;

&lt;p&gt;I got a new page all built out in Astro, and a lot of metrics were better! The first few tests were great.&lt;/p&gt;

&lt;p&gt;But then I noticed that I was still seeing 10 to 30 seconds of total blocking time on some PageSpeed runs on a page WITH ABSOLUTELY no JavaScript.&lt;/p&gt;

&lt;p&gt;It was infuriating, and I was stumped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;At a complete loss at what would be causing it, I went to ChatGPT to see if it had any ideas at what would be going on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foor79zqazx46svymhpai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foor79zqazx46svymhpai.png" alt=" " width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what it suggested:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render blocking fonts or CSS (look big or sync style tags)&lt;/li&gt;
&lt;li&gt;Audit 3rd party embeds or scripts (there were none)&lt;/li&gt;
&lt;li&gt;Images or assets decoding during paint (huge svgs or images can trigger main thread work during initial paint)&lt;/li&gt;
&lt;li&gt;Hydration edge cases&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It gave me these specific tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;switch from .woff to .woff2 fonts and preload them explicitly.&lt;/li&gt;
&lt;li&gt;make sure css is split, purged, and async if possible.&lt;/li&gt;
&lt;li&gt;avoid huge base64-encoded SVGs inline in the html.&lt;/li&gt;
&lt;li&gt;remove ALL 3rd-party scripts temporarily, retest, then add back one by one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, none of those in particular was going on, but the SVG call outs got me wondering. My hero had two SVGs included in it using tailwind, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"relative overflow-hidden bg-[url('/assets/hero-bg.svg')] bg-no-repeat bg-center bg-cover before:content-[''] before:absolute before:bg-[url('/assets/crosshatch.svg')] before:top-0 before:left-0 before:right-0 before:bg-top before:w-full before:h-full before:opacity-50 before:z-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    // rest of the code
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the two background svgs included in there?&lt;/p&gt;

&lt;p&gt;One was a simple cross hatch illustration to add some texture, and the other had some Gaussian Blur in it to make this pretty color background:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwzfs3rx8q0jevnyp2nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwzfs3rx8q0jevnyp2nq.png" alt="The ColorBliss hero with a pretty background gradient" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ran a test removing that background SVG with the blurred colors in it and to my surprise, Total Blocking Time dropped to 160ms.&lt;/p&gt;

&lt;p&gt;I was shocked! I couldn't believe that something I thought was so simple caused such a huge issue rendering issue.&lt;/p&gt;

&lt;p&gt;I removed the SVG, and swapped it out with a relatively simple css gradient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nc"&gt;.hero-gradient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;radial-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;circle&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="m"&gt;36%&lt;/span&gt; &lt;span class="m"&gt;36%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;164&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;146&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;164&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;146&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;30%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;radial-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;circle&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="m"&gt;66%&lt;/span&gt; &lt;span class="m"&gt;15%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;146&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;196&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;146&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;196&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;25%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it worked great. Achieved basically the same look, but with none of the performance issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  But why was the SVG causing 100s of Total Blocking Time?
&lt;/h2&gt;

&lt;p&gt;Well, that solved my problem, but I still didn't quite understand why. Here's what I leanred.&lt;/p&gt;

&lt;p&gt;It turns out that the SVG blur function is incredibly expensive, and my blur radius was ver large.&lt;/p&gt;

&lt;p&gt;Browsers are much more efficient at calculating CSS gradients - they draw it once with lightweight math.&lt;/p&gt;

&lt;p&gt;To process the SVG blur, the browser has to take an offscreen image of my hero area, then for every pixel average a bunch of neighboring pixels, and do that over multiple passes. And I had standard deviation set to 150 (ie 150 smears). It came out to millions of pixel operations just to render that background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Anyways, I learned that SVG usage can be an important place to look for performance issues. My leaning has always been that JavaScript is usually the culprit for most performance issues (and in my experience it usually is!) but if you are having a hard time tracking down your Total Blocking Time, it might be worth taking a look at some of your SVGs for expensive operations like blur.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Web Performance Resources for Front End Developers</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Thu, 05 Jan 2023 00:51:51 +0000</pubDate>
      <link>https://dev.to/benrobertson/web-performance-resources-for-front-end-developers-900</link>
      <guid>https://dev.to/benrobertson/web-performance-resources-for-front-end-developers-900</guid>
      <description>&lt;p&gt;One of the best ways to level up your front end development career is choosing one or more areas to specialize in. Web performance can be directly tied to increases in conversion rates and revenue generated and is a great choice for front end developers looking for an area of specialty since it is so in demand.&lt;/p&gt;

&lt;p&gt;By focusing on performance, a front-end engineer can help ensure that their projects are able to handle large amounts of traffic and provide a smooth and enjoyable user experience.&lt;/p&gt;

&lt;p&gt;And as the web continues to evolve and new technologies emerge, front-end engineers who are experts in performance will be well-positioned to take advantage of these developments and stay ahead of the curve. They will be able to implement cutting-edge performance optimization techniques and stay up-to-date with the latest best practices in the field. This expertise can also lead to career advancement opportunities and help set a front-end engineer apart from their peers.&lt;/p&gt;

&lt;p&gt;There's lots of information out there on web performance, but here are some tried and true docs, courses, books, libraries and tools to dive into if you ar a front end developer serious about getting into web performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources for getting started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://web.dev/vitals/" rel="noopener noreferrer"&gt;Core web vitals | web.dev&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Start here! Core Web Vitals are part of an initiative started by Google to provide high level metrics that act as quality measures of a website. They've picked metrics that represent how quickly your page is visible (First Contentful Paint), how much the layout shifts (Largest Contentful Paint, Cumulative Layout Shift), and how quickly the page is interactive (First Input Delay). These are particularly important because your scores on these metrics affect how your site ranks in Google search results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Courses
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://frontendmasters.com/courses/web-perf/" rel="noopener noreferrer"&gt;Web Performance Fundamentals&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is a great introduction to the fundamentals of web performance: what makes a website (feel) fast or slow, how to measure performance, how to improve performance, and how to optimize specific metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://frontendmasters.com/courses/web-performance/" rel="noopener noreferrer"&gt;JavaScript Performance&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This a deep dive into understanding JavaScript performance. It includes a brief introduction to web performance, a deep dive into JavaScript performance, sections focused on rendering performance, load performance and tools. The rendering performance and load performance sections in particular are most relevant for front end developers looking to understand how to measure, diagnose, and improve performance on the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://moderndevtools.com/" rel="noopener noreferrer"&gt;Modern Dev Tools&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A video based course to develop, debug &amp;amp; performance audit your websites. Level-up your web development skills and create blazing fast experiences. Great hands-on practical guide to using dev tools to identify and fix performance issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.webpagetest.org/learn/lightning-fast-web-performance/" rel="noopener noreferrer"&gt;Lightning-Fast Web Performance&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A free course led by Scott Jehl that starts with the basics and assumes no prior knowledge of performance concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Topics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.patterns.dev/posts/progressive-hydration/" rel="noopener noreferrer"&gt;Progressive Hydration&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Many front end libraries and frameworks provide the ability to generate HTML and CSS on the server to greatly speed up First Contentful Paint and then later have a "hyrdation" step where JavaScript is loaded, parsed, and the UI is made interactive. This article discusses the hydration pattern in more detail - an important concept when trying to decrease time to interactive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Books
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.amazon.com/High-Performance-Browser-Networking-performance/dp/1449344763/ref=sr_1_3?crid=3BH9NNKK8JM03&amp;amp;keywords=web+performance&amp;amp;qid=1671718820&amp;amp;s=books&amp;amp;sprefix=web+performance%2Cstripbooks%2C72&amp;amp;sr=1-3" rel="noopener noreferrer"&gt;High Performance Browser Networking: What every web developer should know about networking and web performance&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Published in 2013, Ilya Grigorik's book on networking covers the foundations of how browsers work--great for becoming an expert in what's going on behind the scenes on every request to your website.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://abookapart.com/products/going-offline" rel="noopener noreferrer"&gt;Going offline&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An introduction into using service workers to make your website resilient to network unreliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://developer.chrome.com/docs/lighthouse/overview/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Lighthouse is an open source tool developed by Google that helps improve the quality of web pages by measuring performance, accessibility, SEO and progressive web apps. You can run Lighthouse tests from inside of Chrom DevTools, CI environments and as a node module.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://googlechrome.github.io/lighthouse/scorecalc/#FCP=1800&amp;amp;SI=5800&amp;amp;FMP=4000&amp;amp;TTI=7300&amp;amp;FCI=6500&amp;amp;LCP=4000&amp;amp;TBT=600&amp;amp;CLS=0.25&amp;amp;device=mobile&amp;amp;version=8" rel="noopener noreferrer"&gt;Lighthouse scoring calculator&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Useful to see how individual metrics play into the larger lighthouse score. Shows you how each metric is weighted, how the weights have evolved over new versions of Lighthouse, and what benchmarks qualify as passing or failing.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;PageSpeed Insights is a tool from Google that along with allowing you to run performance tests measuring Core Web Vitals, combines real world user experience data for users accessing your site, so you can see the real world performance data. PageSpeed Insights also can give you recommendations on where to focus your optimizations. This is a quick and easy way to run a Lighthouse test from on the web.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.webpagetest.org/" rel="noopener noreferrer"&gt;WebPage Test&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;WebPage Test is a tool that lets your run several different kinds of tests: Core Web Vitals, Lighthouse, Visual Comparison between two pages and traceroute. You can also customize the device type, network speed, and location to match your users and get a sense of real world performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://calibreapp.com/" rel="noopener noreferrer"&gt;Calibre&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Calibre automates testing, reporting and alerting, so you don’t have to. You can set up automated testing of specific pages of any website, set up performance budgets to set targets and meet your goals and monitor progress over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.speedcurve.com/" rel="noopener noreferrer"&gt;Speedcurve&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Speedcurve has tools for measuring the performance of your site or app in a lab (also called Synthetic Monitoring) as well as Real User Monitoring, which collects field data from real users on your site to show how they experience the performance of your app. This lets you make targeted, strategic decisions for improving performance for your specific audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Popular libraries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/webpack-contrib/webpack-bundle-analyzer" rel="noopener noreferrer"&gt;Webpack Bundle Analyzer&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Webpack bundle analyzer is a tool that helps you visualize the output of your webpack config and see what actually made it into each of your JavaScript bundles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Resources
&lt;/h2&gt;

&lt;p&gt;Got other recommendations? Share them with us on twitter at &lt;a href="https://twitter.com/remotefrontend" rel="noopener noreferrer"&gt;@remotefrontend&lt;/a&gt; and we'll add them here!&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Color Swatches in Storybook</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Mon, 15 Jul 2019 05:00:00 +0000</pubDate>
      <link>https://dev.to/benrobertson/color-swatches-in-storybook-4aa9</link>
      <guid>https://dev.to/benrobertson/color-swatches-in-storybook-4aa9</guid>
      <description>&lt;p&gt;One thing I had wanted in Storybook for a while was color swatches of all the colors we are using in our project.&lt;/p&gt;

&lt;p&gt;We had color swatches in the style tile, but that’s a pretty static document and it doesn’t really get updated once we start dev, even if colors start to change. Plus, I wanted to be able to display the color variable names we are using along side the hex codes so that hopefully as we discuss colors in the project we can use consistent names.&lt;/p&gt;

&lt;p&gt;Really, I wanted something exactly like what Brad Frost has in his site’s &lt;a href="http://pl.bradfrost.com/" rel="noopener noreferrer"&gt;Pattern Lab&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fcolor-swatches%2Fbrad-frost-pattern-lab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fcolor-swatches%2Fbrad-frost-pattern-lab.png" alt="The color swatches from Brad Frost's pattern lab" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I only wanted to add this if it was going to be maintainable though. I didn’t want us to have to update storybook separately from any color changes we had to make.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exporting SCSS variables for use in JavaScript
&lt;/h2&gt;

&lt;p&gt;With some searching, I found that you can &lt;a href="https://til.hashrocket.com/posts/sxbrscjuqu-share-scss-variables-with-javascript" rel="noopener noreferrer"&gt;export variables from SCSS for use in JS&lt;/a&gt; with Webpack. In fact, I didn’t have to change the Webpack configuration for Storybook at all in order to do this.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;_colors.scss&lt;/code&gt; partial 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;// Defining color variables.
$color-dim-gray: #676767;
$color-zorba: #A19A94;
$color-white-sand: $F1F0ED;
// ... other colors

// Export color variables for use in Webpack.
:export {
  dimGray: $color-dim-gray;
  zorba: $color-zorba;
  whiteSand: $color-white-sand;
  // ... other colors
}

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

&lt;/div&gt;



&lt;p&gt;Now that we have the export defined, we can import this object in a JS file, and loop over each color to create a swatch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Import colors object.
import colors from './_colors.scss';

const colors = () =&amp;gt; {
  return (
    &amp;lt;ul&amp;gt;
      {Object.keys(colors).map((color) =&amp;gt; (
        &amp;lt;li&amp;gt;
          &amp;lt;span
            style={
              backgroundColor: colors[color],
              display: 'block',
              height: '4em',
              marginBottom: '0.3em',
              borderRadius: '5px',
              border: '1px solid lightgray'
            }
          /&amp;gt;
        &amp;lt;span&amp;gt;{color}&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt; // color name
        &amp;lt;span&amp;gt;{colors[color]}&amp;lt;/span&amp;gt; &amp;lt;br /&amp;gt; // hex value
      &amp;lt;/li&amp;gt;
      )
    )
  )
}

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

&lt;/div&gt;



&lt;p&gt;This generates a pretty nice list of colors. A couple things were missing though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the color name displays in camelCase, but that’s not how we reference the colors when we use them in SCSS.&lt;/li&gt;
&lt;li&gt;the colors are in a single big list, and could be organized better.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Computing color names
&lt;/h2&gt;

&lt;p&gt;Based on the swatch above, we would get output that 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;{swatch color}
dimGray
#676767

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

&lt;/div&gt;



&lt;p&gt;What I was aiming for was this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{swatch color}
Dim Gray (color name)
$color-dim-gray (variable name)
#676767 (actual color variable)

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

&lt;/div&gt;



&lt;p&gt;With some help from Stackoverflow, I found a snippet that would help me split the camelcased color name on uppercase letters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let color = 'dimGray';
color.split(/(?=[A-Z])/);
// returns ['dim', 'Gray'];

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

&lt;/div&gt;



&lt;p&gt;This let me do some transformation on the color text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const colorVariable = (color) =&amp;gt; {
    // make an array of the separate words in the color name
  const array = color.split(/(?=[A-Z])/);
  // add a dash in between each word, lowercase everything, and
  // prefix with '$color-'
  return `$color-${array.join('-').toLowerCase()}`;
};

colorVariable('dimGray')
// returns '$color-dim-gray'

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

&lt;/div&gt;



&lt;p&gt;I used a similar method to create the color name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const colorName = (color) =&amp;gt; {
  const array = color.split(/(?=[A-Z])/);
  return `${array.join(' ').toLowerCase()}`;
};

colorName('dimGray')
// returns 'dim gray' (I use css to capitalize this in the component)

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

&lt;/div&gt;



&lt;p&gt;So that gets me all the values I wanted for each color.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grouping colors
&lt;/h2&gt;

&lt;p&gt;Another thing I wanted to do was group the colors together. I wanted to display all the neutrals together, all the brand colors together, and the accent colors together.&lt;/p&gt;

&lt;p&gt;I found that when you are exporting your values from SCSS, you can nest values in the object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Before, without nesting
:export {
  dimGray: $color-dim-gray;
  zorba: $color-zorba;
  whiteSand: $color-white-sand;
  // ... other colors
}

// After, with nesting
:export {
    neutral: {
        dimGray: $color-dim-gray;
      zorba: $color-zorba;
      whiteSand: $color-white-sand;
      // ... other colors
    };
  brand: {
   // brand colors
  };
  accent: {
    // accent colors
    };
}

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

&lt;/div&gt;



&lt;p&gt;Now, when you import the colors into JavaScript, each color key will be prefixed with the nested object key name. For instance - dimGray will come in as &lt;code&gt;neutral-dimGray&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means I need to adjust the methods I created above for parsing the color names. I want to remove the prefix when I display the names, so I need to split on a ‘-‘ and grab what comes after the hyphen, 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;color.split('-')[1]

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

&lt;/div&gt;



&lt;p&gt;So my &lt;code&gt;colorVariable&lt;/code&gt; function becomes this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const colorVariable = (color) =&amp;gt; {
  const array = color.split('-')[1].split(/(?=[A-Z])/);
  return `$color-${array.join('-').toLowerCase()}`;
};

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

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;colorName&lt;/code&gt; function is adjusted similarly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Convert the color key to the color proper name.
const colorName = (color) =&amp;gt; {
  const array = color.split('-')[1].split(/(?=[A-Z])/);
  return `${array.join(' ').toLowerCase()}`;
};

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The components
&lt;/h2&gt;

&lt;p&gt;The last step in this was creating the components for rendering in Storybook.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;&amp;lt;ColorGroup /&amp;gt;&lt;/code&gt; component that displays a collection of &lt;code&gt;&amp;lt;Color /&amp;gt;&lt;/code&gt; components.&lt;/p&gt;

&lt;p&gt;Here’s the display I ended up with in Storybook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fcolor-swatches%2Ffinal-swatches.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fcolor-swatches%2Ffinal-swatches.png" alt="The color swatches, grouped by type in Storybook." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here’s my entire &lt;code&gt;stories.js&lt;/code&gt; file for reference on how it all comes together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import PropTypes from 'prop-types';
import { storiesOf } from '@storybook/react';
import colors from './_colors.scss';

const filterGroup = (filter) =&amp;gt;
  Object.keys(colors).filter((color) =&amp;gt; color.indexOf(filter) === 0);

storiesOf('Global|Colors', module).add('all', () =&amp;gt; (
  &amp;lt;div style={ padding: '20px' }&amp;gt;
    &amp;lt;&amp;gt;
      &amp;lt;h3&amp;gt;Neutral Colors&amp;lt;/h3&amp;gt;
      &amp;lt;ColorGroup group={filterGroup('neutral')} /&amp;gt;
    &amp;lt;/&amp;gt;
    &amp;lt;&amp;gt;
      &amp;lt;h3&amp;gt;Brand Colors&amp;lt;/h3&amp;gt;
      &amp;lt;ColorGroup group={filterGroup('pbr')} /&amp;gt;
    &amp;lt;/&amp;gt;
    &amp;lt;&amp;gt;
      &amp;lt;h3&amp;gt;Accent Colors&amp;lt;/h3&amp;gt;
      &amp;lt;ColorGroup group={filterGroup('accent')} /&amp;gt;
    &amp;lt;/&amp;gt;
  &amp;lt;/div&amp;gt;
));

// Convert the color key to the color variable name.
const colorVariable = (color) =&amp;gt; {
  const array = color.split('-')[1].split(/(?=[A-Z])/);
  return `$color-${array.join('-').toLowerCase()}`;
};

// Convert the color key to the color proper name.
const colorName = (color) =&amp;gt; {
  const array = color.split('-')[1].split(/(?=[A-Z])/);
  return `${array.join(' ').toLowerCase()}`;
};

// A component for displaying individual color swatches.
const Color = ({ color }) =&amp;gt; (
  &amp;lt;li
    style={
      borderRadius: '5px',
      border: '1px solid lightgray',
      padding: '5px'
    }
  &amp;gt;
    &amp;lt;span
      style={
        backgroundColor: colors[color],
        display: 'block',
        height: '4em',
        marginBottom: '0.3em',
        borderRadius: '5px',
        border: '1px solid lightgray'
      }
    /&amp;gt;
    &amp;lt;span style=&amp;gt;{colorName(color)}&amp;lt;/span&amp;gt;{' '}
    &amp;lt;br /&amp;gt;
    &amp;lt;span&amp;gt;{colorVariable(color)}&amp;lt;/span&amp;gt; &amp;lt;br /&amp;gt;
    &amp;lt;span&amp;gt;{colors[color]}&amp;lt;/span&amp;gt; &amp;lt;br /&amp;gt;
  &amp;lt;/li&amp;gt;
);

Color.propTypes = {
  color: PropTypes.string.isRequired
};

// A component for displaying a group of colors.
const ColorGroup = ({ group }) =&amp;gt; (
  &amp;lt;ul
    style={
      display: 'grid',
      gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 175px))',
      gridGap: '20px',
      marginBottom: '40px'
    }
  &amp;gt;
    {group.map((color) =&amp;gt; {
      return &amp;lt;Color color={color} key={color} /&amp;gt;;
    })}
  &amp;lt;/ul&amp;gt;
);

ColorGroup.propTypes = {
  group: PropTypes.array.isRequired
};

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

&lt;/div&gt;



&lt;p&gt;I’m thinking about packaging this up as an addon for Storybook. If you would find that useful - let me know either via email or Twitter!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>storybook</category>
      <category>showdev</category>
    </item>
    <item>
      <title>The Myth of Inaccessible React</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Thu, 13 Jun 2019 05:00:00 +0000</pubDate>
      <link>https://dev.to/benrobertson/the-myth-of-inaccessible-react-25an</link>
      <guid>https://dev.to/benrobertson/the-myth-of-inaccessible-react-25an</guid>
      <description>&lt;p&gt;On Twitter, in Slack, on Discord, or in IRC, or wherever you hang out on the internet, you may have heard some formulation of the following statements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React doesn’t support accessibility&lt;/li&gt;
&lt;li&gt;React makes websites inaccessible&lt;/li&gt;
&lt;li&gt;People should write accessible HTML instead of React&lt;/li&gt;
&lt;li&gt;React is ruining the internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s a somewhat common misperception that JavaScript frameworks and web accessibility don’t mix. React, being one of the largest JavaScript libraries, is often the target.&lt;/p&gt;

&lt;p&gt;In my career, however, I had the interesting experience of being introduced to accessibility and ReactJS at around the same time. I found tooling in React that helped me learn a lot about accessibility that I never would have encountered otherwise.&lt;/p&gt;

&lt;p&gt;And so while I don’t disagree that there are plenty of libraries, websites, apps, etc. written in React that are inaccessible, I do disagree there is something inherent in ReactJS that makes developers build inaccessible sites. In fact, I &lt;strong&gt;love&lt;/strong&gt; the accessibility tooling available in the React ecosystem, and so this post is really about how React can help you make &lt;em&gt;more accessible&lt;/em&gt; websites than you’ve ever made before.&lt;/p&gt;

&lt;p&gt;I’ll outline how you can combine React linting tools, DOM auditing, and Storybook (a component library tool) to provide a really supportive accessibility environment for developers – whether they are accessibility pros or just getting started.&lt;/p&gt;

&lt;p&gt;By the end of this post, you’ll have the following configured for your Gatsby project (or other React project):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in-editor reporting of accessibility errors&lt;/li&gt;
&lt;li&gt;a pre-commit hook for preventing accessibility errors from getting into the repository&lt;/li&gt;
&lt;li&gt;browser console reporting of accessibility errors during development, with links to info on how to resolve the errors&lt;/li&gt;
&lt;li&gt;a component library with built-in accessibility testing so all project stakeholders can hold the team accountable for accessibility issues&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Want to get started right away? I created a Gatsby starter with all these accessibility tools built in. Checkout my &lt;strong&gt;&lt;a href="https://github.com/benjamingrobertson/gatsby-starter-accessibility"&gt;gatsby-starter-accessibility repo&lt;/a&gt;&lt;/strong&gt; that has all these features available out of the box.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tools and Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/evcohen/eslint-plugin-jsx-a11y"&gt;eslint-plugin-jsx-a11y&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you’ve written JavaScript over the past few years, you’ve probably used or at least heard of eslint. If not, now is a great time to get started with it!&lt;/p&gt;

&lt;p&gt;Eslint is a linting utility for JavaScript that helps you catch formatting and syntax errors while you are writing your code. Most editors have some sort of linting configuration built in, which lets you see errors in your editor while you are writing code.&lt;/p&gt;

&lt;p&gt;This is really helpful for keeping code consistent, especially when there’s a lot of people working on a project.&lt;/p&gt;

&lt;p&gt;Eslint also has a really healthy plugin ecosystem. You can include rules specific to the JavaScript framework you are working with (ie, React, Angular, Vue, etc), among others. For React, I typically use the &lt;code&gt;eslint-plugin-react&lt;/code&gt; and the really helpful &lt;a href="https://github.com/evcohen/eslint-plugin-jsx-a11y"&gt;eslint-plugin-jsx-a11y&lt;/a&gt;. This plugin lints your code for known accessibility violations, using &lt;a href="https://github.com/evcohen/eslint-plugin-jsx-a11y#supported-rules"&gt;these rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Having these automated tests run while you are writing code can prevent &lt;em&gt;so many errors&lt;/em&gt;. Even though automated accessibility testing catches only about &lt;a href="https://www.mediacurrent.com/blog/manual-accessibility-testing-why-how/"&gt;20-30% of all accessibility errors&lt;/a&gt;, catching these errors before they make it into a codebase can save time, budget, and energy for doing more manual testing once the code is in the browser.&lt;/p&gt;

&lt;h4&gt;
  
  
  Usage
&lt;/h4&gt;

&lt;p&gt;Here’s how you can get started with accessibility linting in your React project.&lt;/p&gt;

&lt;p&gt;First, we’ll need to install the necessary eslint packages:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install eslint eslint-plugin-react eslint-plugin-jsx-a11y --save-dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;package.json&lt;/code&gt;, add the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"eslintConfig": {
    "parserOptions": {
      "sourceType": "module"
    },
    "env": {
      "node": true,
      "browser": true,
      "es6": true
    },
    "plugins": [
      "react",
      "jsx-a11y"
    ],
    "extends": [
      "eslint:recommended",
      "plugin:react/recommended",
      "plugin:jsx-a11y/recommended"
    ]
}

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



&lt;p&gt;With this added to your &lt;code&gt;package.json&lt;/code&gt;, eslint will use the rules recommended by eslint, react, and the jsx-a11y plugin while you are working.&lt;/p&gt;

&lt;p&gt;You’ll want to make sure your editor is set up to display linting errors in the editor for this to be really useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a pre-commit hook for preventing inaccessible code in the codebase using lint:staged
&lt;/h3&gt;

&lt;p&gt;So, now that we’ve got some accessibility linting set up, and hopefully everyone working on the project has linting turned on in their editor so they can see any errors while they work.&lt;/p&gt;

&lt;p&gt;But you can’t be 100% sure that everyone will be paying attention to the linter. And even if they are, it’s easy to make a quick change, switch files, and any errors will be out of sight, out of mind.&lt;/p&gt;

&lt;p&gt;What we can do as an extra check to prevent inaccessible code from entering the codebase is to add a &lt;em&gt;pre-commit hook&lt;/em&gt; that runs the linting we set up above every time a developer tries to commit code. If an accessibility error is found, an error message will display with the relevant linting error and location of the error, and the commit will be prevented until the developer resolves the issue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_SBSR8tF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/lint-staged-example.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_SBSR8tF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/lint-staged-example.png" alt="lint-staged will run a pre-commit hook that will catch any accessibility errors raised by eslint-plugin-jsx-a11y"&gt;&lt;/a&gt;&lt;em&gt;lint-staged will run a pre-commit hook that will catch any accessibility errors raised by eslint-plugin-jsx-a11y&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Usage
&lt;/h4&gt;

&lt;p&gt;The easiest way to set up pre-commit linting hooks is using the &lt;a href="https://www.npmjs.com/package/lint-staged"&gt;&lt;code&gt;lint-staged&lt;/code&gt; package&lt;/a&gt;. After you’ve got all your eslint configuration set up (from our first step), run the following command in your project directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx mrm lint-staged&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command will install the &lt;a href="https://www.npmjs.com/package/husky"&gt;&lt;code&gt;husky&lt;/code&gt; package&lt;/a&gt; for managing the pre-commit hooks and look in your package.json to automatically setup a pre-commit hook based on your linting configuration.&lt;/p&gt;

&lt;p&gt;A simple configuration that lints all JS files based on the existing eslint configuration in the repo will look like this (from &lt;code&gt;package.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
},
"lint-staged": {
    "*.js": [
      "eslint"
    ]
}

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



&lt;p&gt;You can adjust this as you see fit. For example, sometimes you want to limit linting to certain directories. To run the pre-commit hook only on JS files in the &lt;code&gt;src&lt;/code&gt; directory, you would update the &lt;code&gt;lint-staged&lt;/code&gt; configuration like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"lint-staged": {
    "src/*.js": [
      "eslint"
    ]
}

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



&lt;p&gt;The great thing about &lt;code&gt;lint-staged&lt;/code&gt; is that it only lints the files that are part of the current commit. If for some reason there is some pre-existing errors in another part of the codebase, the commit won’t be prevented–it only prevents new errors from being introduced.&lt;/p&gt;

&lt;h3&gt;
  
  
  react-axe
&lt;/h3&gt;

&lt;p&gt;The great thing about the linting setup we have now is that it will prevent a lot of errors from being introduced into the codebase. It won’t prevent all errors, however. Some errors only exist when several components are used together, or from certain content, and can only be caught in the browser.&lt;/p&gt;

&lt;p&gt;Luckily, we have a solution for this, too. Axe is an open source engine for automated accessibility testing, supported by &lt;a href="https://www.deque.com/"&gt;Deque&lt;/a&gt;. I first became familiar with axe by using their really useful browser extension for &lt;a href="https://www.mediacurrent.com/blog/5-website-accessibility-checkers/"&gt;testing individual pages in the browser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The problem with browser-extension accessibility testing is that they are typically only run &lt;em&gt;after&lt;/em&gt; development is complete. Using the &lt;code&gt;react-axe&lt;/code&gt; library, you can have automated accessibility testing run on every page during development, so developers can get real-time feedback on accessibility issue. This helps make sure that accessibility issues never make it to production, and it also educates developers who may not be accessibility experts on potential pitfalls.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/dequelabs/react-axe"&gt;react-axe&lt;/a&gt; library is an easy to use implementation of the axe engine, specifically for React.&lt;/p&gt;

&lt;h4&gt;
  
  
  Usage
&lt;/h4&gt;

&lt;p&gt;Here’s how to get started using react-axe with Gatsby (&lt;a href="https://github.com/angeloashmore/gatsby-plugin-react-axe"&gt;someone made a Gatsby plugin for it!&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --save gatsby-plugin-react-axe&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;gatsby-plugin-react-axe&lt;/code&gt; to your plugins array in &lt;code&gt;gatsby-config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  siteMetadata: {
    title: 'Gatsby Default Starter',
    description:
      'Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.',
    author: '@gatsbyjs',
  },
  plugins: [
    'gatsby-plugin-react-axe',
    // other plugins go here
  ],
};

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



&lt;p&gt;Now, when the page renders, the plugin will print any accessibility errors to the browser console. Here’s an example, where I’ve put an &lt;code&gt;&amp;lt;h5&amp;gt;&lt;/code&gt; directly underneath an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I09DUJJW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/react-axe-example.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I09DUJJW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/react-axe-example.png" alt="React aXe will show accessibility errors in the console while you are developing."&gt;&lt;/a&gt;&lt;em&gt;React aXe will show accessibility errors in the console while you are developing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can see that in the axe message in the console that it has identified my heading issue: “Heading issues should only increase by one” as a moderate issue. It also includes a link to learn more about &lt;em&gt;why&lt;/em&gt; this is an issue and how to resolve it: &lt;a href="https://dequeuniversity.com/rules/axe/3.2/heading-order"&gt;https://dequeuniversity.com/rules/axe/3.2/heading-order&lt;/a&gt;. And lastly, it displays the specific element that is causing the issue for easy identification.&lt;/p&gt;

&lt;p&gt;This kind of instant feedback is &lt;em&gt;so&lt;/em&gt; important, whether you are an accessibility beginner or even a seasoned pro, catching the automated issues instantaneously can give you more bandwidth to focus on other more involved tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storybook and Accessibility
&lt;/h3&gt;

&lt;p&gt;The last piece of our accessibility workflow has to do with our &lt;a href="https://www.mediacurrent.com/blog/building-components-breaking-it-down/"&gt;component-driven workflow&lt;/a&gt;. For React projects, I have really enjoyed using &lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt; to build and document our front end components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Storybook is an open source tool for developing UI components in isolation for React, Vue, and Angular. It makes building stunning UIs organized and efficient.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Besides having a nice workflow and UI, Storybook has an awesome &lt;a href="https://github.com/storybooks/storybook/tree/master/addons/a11y"&gt;accessibility add-on&lt;/a&gt; that adds a panel to each component in your component library documenting accessibility issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eE9lqFHK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/storybook-preview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eE9lqFHK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/storybook-preview.png" alt="Our storybook configuration has built-in axe tests for each component and a color blindness simulator, provided by the storybook accessibility add-on."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behind the scenes, the add-on actually also uses aXe for testing. This is really nice, because it means that the testing we are using in development is the same as what we are using in the component library. Having the errors highlighted in the component library also helps everyone on our project teams catch accessibility issues as they are browsing the library, either for QA purposes or design inspiration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup
&lt;/h4&gt;

&lt;p&gt;The setup for Storybook is a bit more involved, so if you haven’t used Storybook before, you can checkout the &lt;a href="https://storybook.js.org/docs/guides/guide-react/"&gt;Storybook for React&lt;/a&gt; documentation for a generic React setup.&lt;/p&gt;

&lt;p&gt;If you want to get Storybook running with Gatsby, see &lt;a href="https://www.gatsbyjs.org/docs/visual-testing-with-storybook/"&gt;Visual Testing with Storybook&lt;/a&gt; in the Gatsby docs.&lt;/p&gt;

&lt;p&gt;Once you have Storybook setup, adding the accessibility add-on is pretty straightforward.&lt;/p&gt;

&lt;p&gt;First, install the add-on:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @storybook/addon-a11y --save-dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then add this line to your &lt;code&gt;addons.js&lt;/code&gt; file in your storybook config directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import '@storybook/addon-a11y/register';&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And lastly, add this line in your Storybook &lt;code&gt;config.js&lt;/code&gt; file to automatically add the accessibility panel to all components:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;addDecorator(withA11y);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you run Storybook now, you should now see the accessibility panel (&lt;a href="https://gatsby-starter-accessibility.netlify.com/storybook/?path=/story/header--default"&gt;see a live version here&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eE9lqFHK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/storybook-preview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eE9lqFHK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://benrobertson.io/assets/img/myth-react-inaccessible/storybook-preview.png" alt="Our storybook configuration has built-in axe tests for each component and a color blindness simulator, provided by the storybook accessibility add-on."&gt;&lt;/a&gt;&lt;em&gt;Our storybook configuration has built-in axe tests for each component and a color blindness simulator, provided by the storybook accessibility add-on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As a side note - you can control the order of the tabs in your add-ons panel based on the order that you import add-ons into your &lt;code&gt;addons.js&lt;/code&gt; file, if you want to have the accessibility panel display by default, make sure it is the first line in your &lt;code&gt;addons.js&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;If you didn’t follow along with the setup or just want to get a new project setup quickly with this workflow, checkout the &lt;a href="https://github.com/benjamingrobertson/gatsby-starter-accessibility"&gt;gatsby-starter-accessibility Gatsby starter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create a new Gatsby site with all the configuration I described above out-of-the box with this single line in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx gatsby new my-accessible-project https://github.com/benjamingrobertson/gatsby-starter-accessibility&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or you can checkout the specific configuration in the &lt;a href="https://github.com/benjamingrobertson/gatsby-starter-accessibility"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whether you ran through all the steps above or just run with the starter, you’ll have the following features set up in your Gatsby / React project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in-editor reporting of accessibility errors&lt;/li&gt;
&lt;li&gt;a pre-commit hook for preventing accessibility errors from getting into the repository&lt;/li&gt;
&lt;li&gt;browser console reporting of accessibility errors during development, with links to info on how to resolve the errors&lt;/li&gt;
&lt;li&gt;a component library with built-in accessibility testing so all project stakeholders can hold the team accountable for accessibility issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a complex project with many team members and moving parts, automating accessibility testing will help save time to make sure you can pay more attention to the accessibility tasks that can’t be caught by automated tests.&lt;/p&gt;

&lt;p&gt;Beyond that, tools like this can really help developers level up their accessibility knowledge.&lt;/p&gt;

&lt;p&gt;I know it’s helped me–I hope it helps your team too!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to dive deeper into building accessible websites? Join my free email course:&lt;/em&gt;&lt;br&gt;&lt;br&gt;
📨 &lt;em&gt;&lt;a href="https://dev.to/courses/common-accessibility-mistakes/"&gt;Common accessibility mistakes and how to avoid them&lt;/a&gt;. 30 days, 10 lessons, 100% fun!&lt;/em&gt; 😀 &lt;a href="https://dev.to/courses/common-accessibility-mistakes/"&gt;&lt;em&gt;Sign up here&lt;/em&gt;&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Cover Letter that Got Me a Remote Front End Developer Job</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Fri, 26 Apr 2019 00:58:28 +0000</pubDate>
      <link>https://dev.to/benrobertson/the-cover-letter-that-got-me-a-remote-front-end-developer-job-25f9</link>
      <guid>https://dev.to/benrobertson/the-cover-letter-that-got-me-a-remote-front-end-developer-job-25f9</guid>
      <description>&lt;p&gt;Some people seem to think you can skip the cover letter on job applications. I think they see it as extra work that holds them back from applying for more jobs.&lt;/p&gt;

&lt;p&gt;But that’s looking at it all wrong. If a company is asking for a cover letter, that is your chance to wow them with how unique and wonderful you are. Yes, you can be really creative with your resume too. But if they are giving you the opportunity to send a cover letter, why not take advantage of it?&lt;/p&gt;

&lt;p&gt;Plus, if you come up with a good cover letter template, you can repurpose it for multiple job applications. And guess what – I’m sharing the exact cover letter I sent when I was looking for remote front end work back in the fall of 2017, so you have no excuse not to include one now 😉.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Letter
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Hello Team ${companyName} :),&lt;/p&gt;

&lt;p&gt;I’m a front-end developer based out of Greenville, SC, and I create fun, innovative, accessible, and fast websites. I try to leave every bit I code I touch more readable, modular, performant and accessible than I found it. I’ve worked with major universities across the Southeast US, including Clemson University, University of North Carolina at Chapel Hill, and award-winning work for the University of Kentucky (&lt;a href="https://www.upandup.agency/awards/upup-wins-ucda-excellence-award-university-kentucky-website" rel="noopener noreferrer"&gt;https://www.upandup.agency/awards/upup-wins-ucda-excellence-award-university-kentucky-website&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Beyond front-end coding chops, I’m also passionate about writing and sharing what I’ve learned both with my teammates and the broader web community. I really value the ability to not only perform web development work but also help others improve their craft, because I’ve gained so much from others sharing their skills and knowledge. At Up&amp;amp;Up, I’ve led several different documentation initiatives for my current team, including our use of pattern libraries (&lt;a href="https://css-tricks.com/build-style-guide-straight-sass/" rel="noopener noreferrer"&gt;https://css-tricks.com/build-style-guide-straight-sass/&lt;/a&gt;), a CSS style guide (&lt;a href="https://benrobertson.io/front-end/css-standards" rel="noopener noreferrer"&gt;https://benrobertson.io/front-end/css-standards&lt;/a&gt;), and an internal best practices site. I’m also in the process of writing a book on accessible web design (&lt;a href="https://benrobertson.io/accessibility-for-web-developers/" rel="noopener noreferrer"&gt;https://benrobertson.io/accessibility-for-web-developers/&lt;/a&gt;), to share what I’ve learned over the past several years.&lt;/p&gt;

&lt;p&gt;On a team, I tend to help bring out a clearer sense of purpose and encourage clearer communication, resulting in better team results and a higher sense of satisfaction for all involved.&lt;/p&gt;

&lt;p&gt;Let’s talk about how I could put my eye for detail, practice of web standards and communication skills to work for ${companyName} and friends. Thanks for your consideration.&lt;/p&gt;

&lt;p&gt;Ben Robertson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Anatomy of the Cover Letter
&lt;/h2&gt;

&lt;p&gt;Wow, right? Are you kind of wanting to hire me right now too? 😜&lt;/p&gt;

&lt;p&gt;Feel free to repurpose that letter as you see fit for yourself. What I really recommend though is going through my explanation below and crafting your own very unique letter. I’ve gone through paragraph by paragraph explaining what I’m trying to communicate in the letter and I’ve included ideas that may help you brainstorm your own amazing creative cover letter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Greeting
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Hello Team ${companyName} :),&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For my greeting, I wanted them to know I was writing a cover letter specifically for their team, so I always included the company name.&lt;/p&gt;

&lt;p&gt;I also wanted to communicate that I’m a warm, friendly person, so I included an emoji. I guess if you wanted to be even warmer, you could include a hugging emoji, or something. I like smiling so I included a smiley 🙂.&lt;/p&gt;

&lt;h3&gt;
  
  
  First Paragraph: Introduction and Establishing Credibility
&lt;/h3&gt;

&lt;p&gt;In the first paragraph, I have a few goals. First, I want to introduce myself and show a little more of my personality:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I create fun, innovative, accessible, and fast websites.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I came up with this tagline to show what I value in my work. I’m trying to communicate that web development for me is not just about churning out websites, but that I have a value system behind my work. I’m explaining what’s important to me, and what kind of person I am, in addition to showing that I know that accessibility and performance are important buzzwords 😉.&lt;/p&gt;

&lt;p&gt;I follow up that tagline by going a little deeper:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I try to leave every bit I code I touch more readable, modular, performant and accessible than I found it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here I’m trying to show that I also know that code should be readable so that other developers can work on it and it should be modular for maintainability. In other words: I am team focused.&lt;/p&gt;

&lt;p&gt;I’m also expounding on my value system a little bit by revealing that I do this on all projects. I’m explaining that they won’t have to tell me that readability, modularity, accessibility, and performance are important. I already value those things and I take the initiative on my own to implement them.&lt;/p&gt;

&lt;p&gt;Lastly, I attempt to establish some early credibility by mentioning some work that I have done, focusing on bigger name clients I had worked on. Like any web developer looking back on their code years later, I’m not proud of all the code in those projects, and some of them hadn’t launched at the time (and maybe still haven’t). What’s important here isn’t that I’m pointing to some amazing code I wrote, but simply that I’m establishing credibility. Really what I’m trying to say is: “Hey, I’m a guy you’ve never heard of, but these people think I’m good enough to do work for them so maybe you will too.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Second Paragraph: Highlight some accomplishments
&lt;/h3&gt;

&lt;p&gt;In this second paragraph, I try to highlight some accomplishments that I am particularly proud of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Beyond front-end coding chops, I’m also passionate about writing and sharing what I’ve learned both with my teammates and the broader web community. I really value the ability to not only perform web development work but also help others improve their craft, because I’ve gained so much from others sharing their skills and knowledge. At Up&amp;amp;Up, I’ve led several different documentation initiatives for my current team, including our use of pattern libraries (&lt;a href="https://css-tricks.com/build-style-guide-straight-sass/" rel="noopener noreferrer"&gt;https://css-tricks.com/build-style-guide-straight-sass/&lt;/a&gt;), a CSS style guide (&lt;a href="https://benrobertson.io/front-end/css-standards" rel="noopener noreferrer"&gt;https://benrobertson.io/front-end/css-standards&lt;/a&gt;), and an internal best practices site. I’m also in the process of writing a book on accessible web design (&lt;a href="https://benrobertson.io/accessibility-for-web-developers/" rel="noopener noreferrer"&gt;https://benrobertson.io/accessibility-for-web-developers/&lt;/a&gt;), to share what I’ve learned over the past several years.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a long time, it was so difficult for me to come up with web development accomplishments that I was proud of, because I was always looking at code that I wrote in the past and saw all the things that were bad about it with my 20/20 hindsight.&lt;/p&gt;

&lt;p&gt;Then, one day, I sat down with a blank sheet of paper to write down the things I was proud of as a web developer, and I realized it wasn’t the actual code I had written. What I was really proud of was introducing our team to a new way of doing things, finding a new library we could use, or just always trying to help the team improve.&lt;/p&gt;

&lt;p&gt;And the more I thought about it, I realized that being motivated and able to identify ways for a team to improve was a Really Valuable Attribute to bring to a new company. If you had the choice between hiring a rock star developer that is really good but keeps to herself, or a person who will help improve himself and everyone around him, who would you hire? Who is going to have the biggest impact on your team? Who will have the biggest impact on the bottom line?&lt;/p&gt;

&lt;p&gt;So in this paragraph, I focused on those things. I was really proud of my article on CSS Tricks. I posted some articles on my blog that I had initially written for an internal best practices site, so the writing could be public. And I decided to write a book (I haven’t actually written the book. I’m still planning it – but being someone who has a plan to write a book still sets you apart from others).&lt;/p&gt;

&lt;p&gt;For you, I recommend spending some time thinking about the accomplishments that make you the most proud. They could be some amazing portfolio pieces, or a super cool npm library you wrote. Or, like me, they could be soft skills. Are you a good writer? Are you good at planning, estimating? Did you save your company a bunch of money some how? Try to think from a business perspective rather than from a strictly coding perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Third Paragraph: More soft skills
&lt;/h3&gt;

&lt;p&gt;I highlighted some specific accomplishments in the second paragraph, but now in the third paragraph I’m highlighting some more general soft skills that I have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On a team, I tend to help bring out a clearer sense of purpose and encourage clearer communication, resulting in better team results and a higher sense of satisfaction for all involved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Soft skills are especially important in remote work. Being a good communicator is non-negotiable. At Mediacurrent where I ended up, the team focus is so strong that I think the fact I identified this really made me stand out.&lt;/p&gt;

&lt;p&gt;Think about the unique perspective you bring to team meetings. Think about the questions people ask you. Think about the questions you ask. All these can be clues to the value and role that you play in a team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fourth Paragraph: Brief summary and close
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Let’s talk about how I could put my eye for detail, practice of web standards and communication skills to work for ${companyName} and friends. Thanks for your consideration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the last paragraph I bring it to a clean conclusion. I say “Let’s talk” because that’s what I want: an interview. I see it as a call to action.&lt;/p&gt;

&lt;p&gt;I make the call to action a brief summary of what I’ve laid out above, highlighting one aspect from each paragraph. “Eye for detail” sums up the first paragraph where I talk about always leaving the code better than I found it. I call out web standards because I mentioned accessibility and performance a couple of times in the first and second paragraphs and I want to remind them of that. And I call out “communication skills” as a nod to my third paragraph about soft skills.&lt;/p&gt;

&lt;p&gt;I also include the company name for personalization, and for agencies I added “and friends” to acknowledge I understand that clients are important 🙂. For some applications, if it was a product I used or company that I loved, I also highlighted that fact here.&lt;/p&gt;

&lt;p&gt;The call to action is clear (“let’s talk“) and what’s in it for them is also clear: I’ve laid out a brief summary of the things that are great about me. I add a courteous “Thanks for your consideration” to be professional and grateful and sign off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;So, there you have it: the cover letter template that I used to get the remote front end developer job I have now. I guess you could copy it word for word, but that would be weird.&lt;/p&gt;

&lt;p&gt;But do feel free to take whatever you find helpful from the letter and from my anatomical break down of the letter. I’m writing this because I want you to get your dream job, just like I got mine. Because even though you might be doubting yourself now, getting that new job is within reach for you. And if you are front end developer looking for remote work – try out your new cover letter on our &lt;a href="https://frontendremotejobs.com" rel="noopener noreferrer"&gt;latest remote front end developer job listings&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for a new job?
&lt;/h2&gt;

&lt;p&gt;Front end remote jobs is a curated job board featuring fully remote front end jobs for front end developers. New jobs are added every week.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://frontendremotejobs.com" rel="noopener noreferrer"&gt;View all remote front end jobs =&amp;gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>remote</category>
    </item>
    <item>
      <title>Designing Layouts for Screen Readers</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Tue, 11 Dec 2018 14:00:00 +0000</pubDate>
      <link>https://dev.to/benrobertson/designing-layouts-for-screen-readers-108k</link>
      <guid>https://dev.to/benrobertson/designing-layouts-for-screen-readers-108k</guid>
      <description>&lt;p&gt;It’s easy to think about a layout as being a primarily visual concern. The header goes up top, the sidebar is over here, the call to action is in an overlay on top of the content (just kidding). Grids, borders, spacing and color all portray valuable visual data, but if these hints to the structure of a page are only visible, some users may find your content unintelligible.&lt;/p&gt;

&lt;p&gt;You can experience this first hand if you try using a screen reader on the web. When I fired up VoiceOver on my Mac and took it out for a test drive, I realized that to a screen reader user, a lot pages are just a big heap of ‘content’, missing helpful organizational cues.&lt;/p&gt;

&lt;p&gt;The experience can be kind of like listening to a long rambling story without any indication to what details are important or related to the main thread of the story. Halfway through the story, you aren’t sure whether it’s worth it to keep listening because you don’t know if you’ll even find what it is you’re looking for. In the context of a website, your screen reader might be halfway through reading you a list of 50 sidebar links when you start wondering if there is any valuable content on the site at all.&lt;/p&gt;

&lt;p&gt;Experiences like this are caused by websites that are built with layouts that are only visual. Ideally, however, our visual layouts should point to an underlying organizational model of our content. They should be visual indicators for a conceptual model. The visual indicators are just one way of revealing this model. The Web Accessibility Initiative’s ARIA (Accessible Rich Internet Applications) project provides alternative indicators to users who may need them.&lt;/p&gt;

&lt;p&gt;I’ll walk through how to make use of these indicators to make a simple web page easy to use, navigate and read for users of assistive technology. All the example code is available on &lt;a href="https://github.com/mergeweb/screen-reader-layout-post" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Want to up your accessibility game? Checkout my free email course: &lt;br&gt; ✉️ &lt;a href="/courses/common-accessibility-mistakes/"&gt;Common accessibility mistakes and how to avoid them&lt;/a&gt;.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Layout
&lt;/h2&gt;

&lt;p&gt;Here’s an example of a page with a pretty simple layout. We’ve got a header at the top containing a logo and navigation, some body content, a sidebar off to the right with a related posts list and a list of social media sharing links, a search box below the content and a footer containing the contact info of our business.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mergeweb.github.io/screen-reader-layout-post/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Finitial-layout.png" alt="Screenshot of the initial layout. Click on the image to preview the page."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mergeweb.github.io/screen-reader-layout-post/" rel="noopener noreferrer"&gt;Preview the initial layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mergeweb/screen-reader-layout-post/blob/master/index.html" rel="noopener noreferrer"&gt;View the HTML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visually, the content is pretty well divided, using a simple grid and background colors to distinguish the different elements. If you fire up VoiceOver on this page, you can navigate through the page pretty well using the next element command. The order of elements in the markup pretty much follows the visual order of elements. First we read the header, then the body copy, then the sidebar, then the search box, then the footer. That’s pretty good. If I press &lt;code&gt;CAPS + U&lt;/code&gt; to pull up the VoiceOver menus, I can get a list of all the headers on the page and all the links, and navigate directly to them.&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%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Fheader-menu.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%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Fheader-menu.png" alt="VoiceOver will display a navigable list of all headings on a page."&gt;&lt;/a&gt;&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%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Flink-menu.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%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Flink-menu.png" alt="VoiceOver will also display a navigable list of all links on a page."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just by using well-structured HTML, simple grouping with &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements and a good use of heading tags we’ve got a decent experience. It’s better than the rambling story websites I mentioned above, but it could be even better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skip Link
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mergeweb.github.io/screen-reader-layout-post/skip-link.html" rel="noopener noreferrer"&gt;Preview the skip link layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mergeweb/screen-reader-layout-post/blob/master/skip-link.html" rel="noopener noreferrer"&gt;View the HTML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First we’ll add a skip-link as the first item of the page. A skip link is a very common accessibility feature that allows users to skip past lengthy lists of links and other information repeated on every web page directly to the main content of the current page.&lt;/p&gt;

&lt;p&gt;It’s a link that is the first element in the tab order of the page. It is typically visually hidden, but when focused, it appears on-screen. To visually hide the link, we’ll add the following CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.skip {
    clip: rect(1px, 1px, 1px, 1px);
    position: absolute !important;
    height: 1px;
    width: 1px;
    overflow: hidden;
    word-wrap: normal !important; /* Many screen reader and browser combinations announce broken words as they would appear visually. */
}

/* Display the link on focus. */
.skip:focus {
    background-color: #fff;
    border-radius: 3px;
    box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
    clip: auto !important;
    color: #888;
    display: block;
    font-weight: bold;
    height: auto;
    left: 5px;
    line-height: normal;
    padding: 15px 23px 14px;
    text-decoration: none;
    top: 5px;
    width: auto;
    z-index: 100000;
}

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

&lt;/div&gt;



&lt;p&gt;The link location of the skip link needs to be an &lt;code&gt;id&lt;/code&gt; pointing to the main content of the page. In our case, I added &lt;code&gt;id="main"&lt;/code&gt; to the &lt;code&gt;&amp;lt;div class="content"&amp;gt;&lt;/code&gt; section and gave the skip link a url of &lt;code&gt;href="#main"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you visit the &lt;a href="https://mergeweb.github.io/screen-reader-layout-post/skip-link.html" rel="noopener noreferrer"&gt;skip link page&lt;/a&gt; and hit your Tab key, the link should display. If you fire up VoiceOver and start navigating through the page, the skip link should be the first thing you come across, and clicking it should trigger VoiceOver to start reading the main content of the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  WCAG Techniques Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/WCAG20-TECHS/G1.html" rel="noopener noreferrer"&gt;Skip Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this step, we’ve allowed users to skip straight to the meat of our page, but beyond easily accessing the main content, they still don’t have a good conceptual map of the rest of the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  ARIA Roles and Landmarks
&lt;/h2&gt;

&lt;p&gt;One way to provide users with a conceptual map of the page is by using semantic HTML5 elements like &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;. These elements have built in data associated with them that is parsed by browsers and screen readers. They create &lt;a href="https://www.w3.org/WAI/GL/wiki/Using_ARIA_landmarks_to_identify_regions_of_a_page" rel="noopener noreferrer"&gt;landmarks&lt;/a&gt; on a web page. By using these elements judiciously in place of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements, we can provide extra information to assistive technology devices and help the user build a conceptual map of our page.&lt;/p&gt;

&lt;p&gt;I’ve maintained the same layout as before, but I’ve swapped some divs for some semantic HTML5 elements. I’ve also added a &lt;code&gt;role&lt;/code&gt; attribute to the search component. Alternatively, you could keep all the divs and add a &lt;code&gt;role&lt;/code&gt; instead of swapping them out for the new HTML5 elements. (See &lt;a href="https://www.w3.org/TR/wai-aria/roles#landmark_roles" rel="noopener noreferrer"&gt;the w3 guidelines for ARIA roles&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  - &lt;a href="https://mergeweb.github.io/screen-reader-layout-post/aria-roles.html" rel="noopener noreferrer"&gt;Preview the updated layout&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mergeweb/screen-reader-layout-post/blob/master/aria-roles.html" rel="noopener noreferrer"&gt;View the updated HTML&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div class="header"&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;header class="header"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div class="main-navigation"&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;nav class="main-navigation"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div class="content"&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;main class="content"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div class="sidebar"&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;aside class="sidebar"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div class="related-posts"&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;section class="related-posts"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div class="search"&amp;gt;&lt;/code&gt; becomes &lt;code&gt;&amp;lt;div class="search" role="search"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now when I fire up VoiceOver and press &lt;code&gt;CAPS + U&lt;/code&gt;, I get a new Landmarks menu. Inside this menu you can see the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;banner&lt;/li&gt;
&lt;li&gt;navigation&lt;/li&gt;
&lt;li&gt;main&lt;/li&gt;
&lt;li&gt;complementary&lt;/li&gt;
&lt;li&gt;search&lt;/li&gt;
&lt;li&gt;content information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selecting any of these menu items takes the user straight to that element, so they can easily navigate through the different elements of a page. If they are at the bottom of the page, they can easily get back to the main navigation in the header via the Landmarks menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  WCAG Techniques Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/WCAG20-TECHS/ARIA11.html" rel="noopener noreferrer"&gt;ARIA Landmarks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve dramatically increased the navigability of our page and provided an initial map to our users, but we’re missing a few things to make this experience really awesome. First, the names of our site sections are fairly generic. We aren’t exactly sure just from listening to the menu what might be in any of the elements. Second, some elements aren’t easily navigable. For instance, our sidebar components are all grouped under the label ‘complementary’.&lt;/p&gt;

&lt;p&gt;We can add some well-thought out ARIA labels to make this experience even better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Appropriate ARIA Labels
&lt;/h2&gt;

&lt;p&gt;By peppering in some ARIA labels we can give the user an even more detailed conceptual map of our layout.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mergeweb.github.io/screen-reader-layout-post/aria-labels.html" rel="noopener noreferrer"&gt;Preview the updated layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mergeweb/screen-reader-layout-post/blob/master/aria-labels.html" rel="noopener noreferrer"&gt;View the updated HTML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this next iteration, I’ve added the following labels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;nav class="main-navigation"&amp;gt;&lt;/code&gt; now has an &lt;code&gt;aria-label&lt;/code&gt; of &lt;code&gt;Primary Navigation&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;main class="content"&amp;gt;&lt;/code&gt; now has an &lt;code&gt;aria-labelledby&lt;/code&gt; attribute of &lt;code&gt;main-title&lt;/code&gt; and its &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; has an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;main-title&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;aside class="sidebar"&amp;gt;&lt;/code&gt; now has an &lt;code&gt;aria-labelledby&lt;/code&gt; attribute of &lt;code&gt;sidebar-title&lt;/code&gt; and its &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; has an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;sidebar-title&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Both &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; elements in the sidebar now have an appropriate ARIA label.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s fire up VoiceOver again and pull up our Landmark menu with &lt;code&gt;CAPS+U&lt;/code&gt;. Now we see that the ARIA labels we provided display next to each of our generic menu items. We also have a few extra menu items, because the &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; elements we provided labels for (Related Posts, Share Links), now have their own menu items.&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%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Flandmarks-detail.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%2Fbenrobertson.io%2Fassets%2Fimg%2Fdesigning-layouts-for-sr%2Flandmarks-detail.png" alt="The VoiceOver landmarks menu now shows detailed information about each of the sections on our page, including the aria-labels that we provided."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now an assistive technology user has an equal (and maybe even better) conceptual map of the content and actions they can take on this website compared to a non-assistive technology user. They can get a quick overview of everything on the site, easily navigate to the section of the page they want, and quickly find what they are looking for.&lt;/p&gt;

&lt;h3&gt;
  
  
  WCAG Techniques Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/WCAG20-TECHS/ARIA11.html" rel="noopener noreferrer"&gt;Landmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/WCAG20-TECHS/ARIA13.html" rel="noopener noreferrer"&gt;Labelling Landmarks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;With a combination of well-structured HTML markup, thoughtful use of ARIA roles and a careful labeling of site sections using ARIA labels, we’re able to create a user experience for assistive technology users that rivals the experience of non-assistive technology users. We were able to take the conceptual map that was implicit in our visual layout and expose it to assistive technology.&lt;/p&gt;

&lt;p&gt;You may find holes in your conceptual map or sections that unnecessarily have the same function. The process can help you clarify your designs, identify areas that might not make sense conceptually or visually, and improve your design for all users of your site.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to dive deeper into building accessible websites? Join my free email course:&lt;/em&gt; 📨 &lt;em&gt;&lt;a href="https://dev.to/courses/common-accessibility-mistakes/"&gt;Common accessibility mistakes and how to avoid them&lt;/a&gt;. 30 days, 10 lessons, 100% fun!&lt;/em&gt; 😀 &lt;a href="https://dev.to/courses/common-accessibility-mistakes/"&gt;&lt;em&gt;Sign up here&lt;/em&gt;&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Short Guide to Screen Reader Friendly Code</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Tue, 16 Oct 2018 13:00:00 +0000</pubDate>
      <link>https://dev.to/benrobertson/a-short-guide-to-screen-reader-friendly-code-3pgf</link>
      <guid>https://dev.to/benrobertson/a-short-guide-to-screen-reader-friendly-code-3pgf</guid>
      <description>&lt;p&gt;If you want to have an accessible website you &lt;em&gt;have&lt;/em&gt; to make sure it works with screen readers.&lt;/p&gt;

&lt;p&gt;What is a screen reader, you ask? A &lt;strong&gt;screen reader&lt;/strong&gt; is a piece of software that reads the contents of a computer screen out loud to a person and lets that person interact with the content via their keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does it matter?
&lt;/h2&gt;

&lt;p&gt;Why must websites work with a screen reader? Screen reader software gives people with visual impairments access to the wealth of information on the internet. If you want to build the internet the way it was meant to be, then you want your websites to be &lt;a href="https://benrobertson.io/accessibility/what-is-website-accessibility" rel="noopener noreferrer"&gt;accessible to as many people as possible&lt;/a&gt;. Web design after all, is &lt;a href="https://benrobertson.io/accessibility/principles-getting-started-website-accessibility#principle-1-web-design-is-more-than-graphic-design" rel="noopener noreferrer"&gt;so much more than just visual design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, HTML is accessible to screen readers by default! Unfortunately, writing good HTML often falls by the wayside for a host of reasons. Most people don’t know better. I know &lt;em&gt;I&lt;/em&gt; didn’t know any better when I got started as a web developer.&lt;/p&gt;

&lt;p&gt;When we developers write bad HTML (&lt;a href="https://benrobertson.io/accessibility/javascript-accessibility" rel="noopener noreferrer"&gt;and JavaScript&lt;/a&gt;, and CSS) it can render a website unintelligible, unusable and downright worthless to people who need a screen reader.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Want to up your accessibility game? Checkout my free email course: ✉️ &lt;a href="https://benrobertson.io/courses/common-accessibility-mistakes/" rel="noopener noreferrer"&gt;Common accessibility mistakes and how to avoid them&lt;/a&gt;.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Since HTML is accessible to screen readers by default, using semantic HTML will go a long way in making any site usable for people who use screen readers. To ensure an excellent experience for people using screen readers, follow these five tips:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pay attention to the title tag&lt;/li&gt;
&lt;li&gt;Make sure your site is navigation-friendly&lt;/li&gt;
&lt;li&gt;Every image needs an alt attribute&lt;/li&gt;
&lt;li&gt;Role Attributes and Landmarks&lt;/li&gt;
&lt;li&gt;Avoid click event listeners on &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Pay attention to the title tag
&lt;/h2&gt;

&lt;p&gt;The title tag is the first thing a screen reader user will hear when they reach a page. It helps people know what the page is about, and whether they are in the right place. When they come back to the browser tab later, they will know what page they are on.&lt;/p&gt;

&lt;p&gt;It may seem obvious, but title tags can get lost in the shuffle in a large project. A common mistake I see is setting a default title tag that is used on every page. This process causes every page to have the same title.&lt;/p&gt;

&lt;p&gt;Imagine a user who is trying to compare several cars to decide which one to buy. They might have 15 tabs open, each containing reviews, make and model details, trim levels, and pricing information. Imagine if they all had the same title, how would they know which one to open? It can be &lt;em&gt;so&lt;/em&gt; annoying especially if you are relying only on a screen reader to tell you the contents of a tab, so make sure all pages have an informative and unique title!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Make sure your site is navigation-friendly
&lt;/h2&gt;

&lt;p&gt;Another important consideration is that screen readers provide different ways for users to navigate the website. A person using a screen reader may not even use your carefully constructed mega menu. In fact, if it’s not accessible, they probably &lt;strong&gt;won’t be able to&lt;/strong&gt;. Let’s look at a few of the ways they might navigate a website instead:&lt;/p&gt;

&lt;h3&gt;
  
  
  Headings
&lt;/h3&gt;

&lt;p&gt;One option is a list of headings. Heading tags form the outline of a web page, so they are a natural navigation method for screen readers to use. If you are using heading tags correctly and following correct nesting order (H2 under H1 only, H3 under H2 only, etc), then the outline of your site will be a clear overview of the content on the page. This order is why it is important to &lt;strong&gt;use heading tags as structural elements, not simply stylistic elements&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  List of All Links
&lt;/h3&gt;

&lt;p&gt;Another option is a list of links. Screen reader users can get a list of all links on the page, and then navigate through them, skipping all content that is not a link. This navigation process is one major reason to eliminate link text like “read more” or “click here”. If there are 50 links that say “read more”, a screen reader user navigating via the links menu is going to skip all of them because they have no context and are essentially meaningless.&lt;/p&gt;

&lt;p&gt;Make sure the link text accurately reflects the linked content so people can find the content they are looking for, or &lt;a href="https://benrobertson.io/accessibility/principles-getting-started-website-accessibility#aria-attributes" rel="noopener noreferrer"&gt;use an aria-label to provide more contextual link content&lt;/a&gt; for screen readers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Landmarks
&lt;/h3&gt;

&lt;p&gt;Perhaps one of the neatest navigation options is the landmarks menu. Screen readers will interpret elements like &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; as landmark elements, and let users navigate directly to them. You can see how these landmarks are displayed and read to users in macOS VoiceOver below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Flandmarks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Flandmarks.png" alt="An example of the landmarks menu in macOS Voiceover" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://benrobertson.io/accessibility/understanding-layout-for-screen-readers" rel="noopener noreferrer"&gt;thoughtful use of landmarks&lt;/a&gt; can give a website extra organization that will help people find information more easily. Beyond making the site more accessible, making decisions about using landmarks on a site can provide clarity about the true purpose of different elements on a site.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Every image needs an alt attribute
&lt;/h2&gt;

&lt;p&gt;If an image is missing an alt (alternative text) attribute, screen readers will read the image file name. That can be very annoying for computer generated names like &lt;em&gt;201018acn300x450.jpeg&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If an alt attribute exists and has text in it, the text will be read. No need to include “Photo of” or “Image of”, the screen reader will indicate that it is an image.&lt;/p&gt;

&lt;p&gt;If an alt attribute exists and is empty, the screen reader will skip this image entirely. If the image is not content, is purely decorative, and not important for understanding content or functionality, an empty alt attribute can be a good strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Role Attributes and Landmarks
&lt;/h2&gt;

&lt;p&gt;As was mentioned earlier, screen readers allow people to navigate via landmarks. Here is an example landmarks menu from a site that uses a combination of &lt;a href="https://benrobertson.io/accessibility/understanding-layout-for-screen-readers#aria-roles-and-landmarks" rel="noopener noreferrer"&gt;landmark elements, role attributes&lt;/a&gt;, and &lt;a href="https://benrobertson.io/accessibility/principles-getting-started-website-accessibility#aria-attributes" rel="noopener noreferrer"&gt;aria-labels&lt;/a&gt; to create a clean, easily understandable landmarks menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Flandmarks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Flandmarks.png" alt="An example of the landmarks menu in macOS Voiceover" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; element for example, which would be used to define navigation element. The screen reader will note to the user by default that this is a navigation element. This can be taken a step further and provide an aria-label (&lt;code&gt;&amp;lt;nav aria-label=”Primary Menu”&amp;gt;&lt;/code&gt;) which will tell the user that this is the Primary Menu navigation.&lt;/p&gt;

&lt;p&gt;In the example screenshot above, the Sidebar, Related Posts, Social Share Links, and Search sections of the page are labelled to make them more easily discoverable. It makes the entire site easier to understand and navigate.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Avoid click event listeners on &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements
&lt;/h2&gt;

&lt;p&gt;A common pattern for interactive sites is to make the website do something when a user clicks on an element. Click on this text or button, and the menu expands. This functionality is accomplished with Javascript by listening for click events, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interactiveElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div.className&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;interactiveElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For screen readers, listening for clicks on HTML div elements can be detrimental because divs convey no semantic information to screen readers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here are the problems:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;VoiceOver and other screen readers present them as a generic text element.&lt;/li&gt;
&lt;li&gt;Users will not be able to tell that they can click on the element.&lt;/li&gt;
&lt;li&gt;If a user is using their keyboard to navigate a page, they will never land on a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; because it is not focusable. The div will never receive focus, and their browser will skip over it.&lt;/li&gt;
&lt;li&gt;Also, there is another menu in voiceover that shows all the interactive buttons on the page. A div with a click event will not show up in this button menu.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s are inaccessible to screen readers.&lt;/p&gt;

&lt;p&gt;There are workarounds for making divs more accessible, but the fastest route is to just use the HTML &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element. When the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element is listening for click events you get all the functionality screen readers and keyboard users need to use that element: the screen reader will tell the user it’s a button, users can navigate directly to the button, and the button will have the same functionality with a mouse or a keyboard. The &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element does a lot of the heavy lifting of making interactive elements accessible, so use them.&lt;/p&gt;

&lt;p&gt;(Need help styling your buttons? &lt;a href="https://codepen.io/hankchizljaw/pen/Vxpjvo" rel="noopener noreferrer"&gt;Andy Bell’s Button Pal is a great CSS resource&lt;/a&gt;!)&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s not too scary, right?
&lt;/h2&gt;

&lt;p&gt;I hope you see from these 5 tips that starting on accessibility doesn’t have to be scary. For the most part, &lt;em&gt;accessibility is about doing the simple things the right way&lt;/em&gt;. We want to make the web easier to use and understand for everybody, no matter what.&lt;/p&gt;

&lt;p&gt;When you get to the bottom of it, this is the core of web accessibility, and really the driving principle of the web in general: ensuring that anybody can access, use, and understand the information, regardless of the method they use to interact with the internet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to dive deeper into building accessible websites? Join my free email course:&lt;/em&gt; 📨 &lt;em&gt;&lt;a href="https://benrobertson.io/courses/common-accessibility-mistakes/" rel="noopener noreferrer"&gt;Common accessibility mistakes and how to avoid them&lt;/a&gt;. 30 days, 10 lessons, 100% fun!&lt;/em&gt; 😀 &lt;a href="https://benrobertson.io/courses/common-accessibility-mistakes/" rel="noopener noreferrer"&gt;&lt;em&gt;Sign up here&lt;/em&gt;&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>beginners</category>
      <category>frontend</category>
      <category>devtips</category>
    </item>
    <item>
      <title>4 Principles for Getting Started with Website Accessibility</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Wed, 08 Aug 2018 16:34:50 +0000</pubDate>
      <link>https://dev.to/benrobertson/4-principles-for-getting-started-with-website-accessibility-1d15</link>
      <guid>https://dev.to/benrobertson/4-principles-for-getting-started-with-website-accessibility-1d15</guid>
      <description>&lt;p&gt;When I was entering the front end developer ranks, no one talked to me about &lt;a href="https://benrobertson.io/accessibility/" rel="noopener noreferrer"&gt;accessibility&lt;/a&gt;. I didn't know you could break the law for having an inaccessible website, until one day a university client came to my team to help them perform an accessibility audit. Man was I in over my head.&lt;/p&gt;

&lt;p&gt;I started digging in and doing research, but found a lot of the documentation intimidating. Some of it was over my head, and there was so much to digest, but I eventually made my way through. (Well, I'm actually still making my way through).&lt;/p&gt;

&lt;p&gt;I've since learned that accessibility doesn't have to be intimidating, and can even be fun.&lt;/p&gt;

&lt;p&gt;What would have helped me at the beginning were a few practical principles to help me grasp the basics.&lt;/p&gt;

&lt;p&gt;So let me share with you:&lt;/p&gt;

&lt;h2&gt;
  
  
  Ben's Homegrown Web Accessibility Principles
&lt;/h2&gt;

&lt;p&gt;They're not rules.&lt;/p&gt;

&lt;p&gt;They are mental shifts that I had to make when I started developing accessible websites.&lt;/p&gt;

&lt;p&gt;Let's get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 1: Web Design is more than Graphic Design
&lt;/h2&gt;

&lt;p&gt;When I started my first web job, I was handed a picture of a website and asked to turn it into a website.&lt;/p&gt;

&lt;p&gt;After I did that, the designers then meticulously compared my website to their picture of a website and told me all the mistakes I made.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Line height should be 18px, not 16.&lt;/p&gt;

&lt;p&gt;This gray is the wrong light gray. It should be light-light gray.&lt;/p&gt;

&lt;p&gt;The box-shadow blur is off by a pixel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stuff like that. They were very impressive and I learned a ton.&lt;/p&gt;

&lt;p&gt;But none of us really considered that the web is not a controlled medium. We were so concerned with the visual elements of the work, that we didn't consider how the site might perform on a $99 Android phone over 3G or for someone who was color blind, or someone who couldn't see at all.&lt;/p&gt;

&lt;p&gt;And the fact that the web can be accessed by these people in those situations is what makes web design so much more than graphic design.&lt;/p&gt;

&lt;p&gt;So instead of focusing merely on visual elements, I split my work into three main tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three Tasks of Web Design
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Task 1: Write good (read: semantic) markup.
&lt;/h4&gt;

&lt;p&gt;The first task is to write good markup.&lt;/p&gt;

&lt;p&gt;This means organizing the content on the page well. Using HTML the way is was meant to be used. HTML is accessible by default. So if we get this right from the beginning our jobs are so much easier. We'll spend some more time on this a little later.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task 2: &lt;em&gt;Enhance&lt;/em&gt; the markup with CSS.
&lt;/h4&gt;

&lt;p&gt;The second task is to use CSS to &lt;strong&gt;enhance&lt;/strong&gt; the excellent markup that we have written.&lt;/p&gt;

&lt;p&gt;CSS should be used to emphasize the meaning of your content. It should make it more meaningful, more impactful. But you've got to use the right HTML to begin with or else your job will be a lot harder.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task 3: &lt;em&gt;Layer&lt;/em&gt; interactivity on your HTML and CSS with JavaScript
&lt;/h4&gt;

&lt;p&gt;And the third task is to layer interactivity over the structure and style with JavaScript.&lt;/p&gt;

&lt;h4&gt;
  
  
  Before and After
&lt;/h4&gt;

&lt;p&gt;The difference between this approach and how I used to build websites is that I used to just reach for the element that was easiest to style, and use that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I need big text, so I'll use an h1.&lt;/p&gt;

&lt;p&gt;I have a complicated accordion interface, so I'll use a bunch of divs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stuff like that. But that only focuses on the &lt;em&gt;visual&lt;/em&gt; aspects. To build accessible websites, we need to think about more than just how closely the site matches the picture. It's more than visual design or graphic design. That's why we call it web design.&lt;/p&gt;

&lt;p&gt;This brings us to principle 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 2: Be ASAP: As Semantic as Possible.
&lt;/h2&gt;

&lt;p&gt;Here's how I recommend doing this.&lt;/p&gt;

&lt;p&gt;Every time you start typing &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;Stop.&lt;/p&gt;

&lt;p&gt;Look in the mirror.&lt;/p&gt;

&lt;p&gt;And ask yourself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Could I use a more semantic element?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;How do you know if there is a more semantic element to use?&lt;/p&gt;

&lt;p&gt;The Mozilla Development Network has a page of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element" rel="noopener noreferrer"&gt;all HTML elements organized by their purpose&lt;/a&gt;. (This reference is awesome - use it!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fcontent-sectioning.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fcontent-sectioning.png" alt="Screenshot of the content sectioning table from the MDN documentation" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at some of the semantic alternatives we have for &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternatives to &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you have a stand alone section of a page, consider using the &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;If you have a blog, news article, forum post, or any kind of self-contained piece of content, you could use an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Got several components of the same kind next to each other? Consider using an ordered or unordered list (&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Got a top section on your blog post with title and metadata? Use a &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;. Got a bottom section with tags and such? Use a &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Got a sidebar? Use an &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Got something that needs to be clickable - use a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. This one is important. If it needs to be clickable and is not a link, you should probably use a button.&lt;/p&gt;

&lt;p&gt;Let me repeat that: If it needs to be clickable and is not a link, you should probably &lt;a href="https://benrobertson.io/accessibility/javascript-accessibility#1-use-the-button-element-for-anything-that-users-click-on" rel="noopener noreferrer"&gt;use a button&lt;/a&gt;. We'll talk more about that later.&lt;/p&gt;

&lt;p&gt;Just remember: Be ASAP. As semantic as possible].&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 3: Web Sites Should Look Good Naked
&lt;/h2&gt;

&lt;p&gt;What I mean by this is that if you remove all the CSS from your page, your website should still be readable and usable.&lt;/p&gt;

&lt;p&gt;This principle is really driving home the point of principle 2: Be as semantic as possible.&lt;/p&gt;

&lt;p&gt;Think about it like this: if your markup is semantic, then you are using elements that convey meaning. And that means the browser will provide affordances and signifiers for the meaning and / or functionality of your markup.&lt;/p&gt;

&lt;p&gt;So the "naked test" is really a test of how semantic your markup is.&lt;/p&gt;

&lt;p&gt;Your markup should look like a well-structured outline, like we used to make in school for research papers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you check this?
&lt;/h3&gt;

&lt;p&gt;Here is the code. If you pop this into your dev tools console, it will strip out everything from the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your document, including the styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it's doing is targeting the document head and then removing all its children.&lt;/p&gt;

&lt;p&gt;Most of the time, I use this as a little bookmarklet in my browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this as a bookmarklet, add a new bookmark in your browser. In the url field, copy and paste the code above instead of a URL. Now, you can click this bookmark while on any site and it will remove all the styles from the document head.&lt;/p&gt;

&lt;p&gt;Let's look at an example of this in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Google Sign-in form
&lt;/h3&gt;

&lt;p&gt;I think everyone is likely familiar with the Google sign-in form. It's got a title, the email input, and a couple buttons for Forgot Email, Create Account, and Next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fgoogle-sign-in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fgoogle-sign-in.png" alt="The Google sign-in form circa July 2018" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what happens when we look at it naked?&lt;/p&gt;

&lt;h3&gt;
  
  
  Naked Google Sign-in form
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fgoogle-sign-in-naked.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbenrobertson.io%2Fassets%2Fimg%2Fgoogle-sign-in-naked.png" alt="The Google sign-in form circa July 2018, with styles stripped away" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After we remove the styles, we still have the nice Sign in heading, so we know what this page is about.&lt;/p&gt;

&lt;p&gt;We have a few inputs, but the labels aren't exactly clear.&lt;/p&gt;

&lt;p&gt;And...where did our buttons go?&lt;/p&gt;

&lt;p&gt;If you look closely, you can see that what used to be the "Next" and "Forgot Email" and "Create Account" buttons now all appear to be normal text.&lt;/p&gt;

&lt;p&gt;We've got three inputs now, instead of the one we had before and the labels appear to be after them.&lt;/p&gt;

&lt;p&gt;The Next and Create Account buttons swapped positions.&lt;/p&gt;

&lt;p&gt;So everything is still here, but I'd say the main concern I have is with the buttons not really being buttons. They aren't clear.&lt;/p&gt;

&lt;p&gt;And let me just say that just because Google isn't using the HTML &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element doesn't mean this form is inherently inaccessible, it just means they have to do a lot more work with JavaScript and managing keyboard interactions that the browser would typically do for you.&lt;/p&gt;

&lt;p&gt;I typically use the naked test as a gut check for myself. Just because a site fails the Naked Test doesn't mean the site is necessarily inaccessible. You can fail the naked test and still have an accessible website. But the Naked Test will reveal areas where you aren't using semantic markup, and these areas may need special accessibility attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to look for during the Naked Test
&lt;/h3&gt;

&lt;p&gt;Here is what I look for when I run this test.&lt;/p&gt;

&lt;p&gt;First, I check to make sure the structure of the site makes sense. Are things in the right order? Does each section have a clear heading with the right level of heading tag?&lt;/p&gt;

&lt;p&gt;Next, does the content appear to be organized? Can I skim the page and get an idea of the content as if I were skimming an outline?&lt;/p&gt;

&lt;p&gt;Third, I look to see if interactive elements look to be interactive. If I've created a bunch of interactive elements using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s, they won't appear interactive. Then I'll know to spend a little more time checking the &lt;a href="https://benrobertson.io/accessibility/javascript-accessibility#2-plan-for-common-keyboard-interactions" rel="noopener noreferrer"&gt;keyboard functionality&lt;/a&gt; of those elements for accessibility.&lt;/p&gt;

&lt;p&gt;And lastly, I want to make sure inputs have clear labels.&lt;/p&gt;

&lt;p&gt;That about sums up the naked test. To reiterate, the point of the test is to reveal weaknesses in the semantics of your site and point out the areas where you'll need to spend a little more time testing to make sure those components are accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principle 4: Talk to your computer
&lt;/h2&gt;

&lt;p&gt;Here is my fourth and last homegrown principle: Talk to your computer.&lt;/p&gt;

&lt;p&gt;Ok, maybe don't actually talk out loud to your computer. What I mean here is to &lt;em&gt;communicate&lt;/em&gt; with your computer - give the browser some context using &lt;a href="https://www.w3.org/WAI/standards-guidelines/aria/" rel="noopener noreferrer"&gt;ARIA attributes&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ARIA Attributes
&lt;/h3&gt;

&lt;p&gt;ARIA stands for Accessible Rich Internet Applications, and there are ARIA states, roles, and properties that tell the browser certain things about your web page, if you choose to use them.&lt;/p&gt;

&lt;p&gt;I am suggesting that you use them where &lt;em&gt;appropriate&lt;/em&gt;. They won't be visible to users, but they will be used by the browser and by screen-readers to provide a little extra context to users behind the scenes.&lt;/p&gt;

&lt;p&gt;Here are some examples:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;aria-label&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;aria-label&lt;/code&gt; attribute can be added as an attribute of an HTML element to tell a screen reader what it is. I use these on links a lot, to provide extra context to screen reader users for where the link is going.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;aria-expanded&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://benrobertson.io/accessibility/javascript-accessibility#3-manage-aria-statesy" rel="noopener noreferrer"&gt;&lt;code&gt;aria-expanded&lt;/code&gt; attribute&lt;/a&gt; tells whether an element is open or closed. You might use this on a hamburger button that controls your main navigation. When the screen reader user focuses on the button with an &lt;code&gt;aria-expanded&lt;/code&gt; value of false, the screen reader will say something like "Main Menu, collapsed button" and they will know they can open the menu.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;aria-describedby&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;aria-describedby&lt;/code&gt; attribute points to an element that describes the current element. If you want to add some error text to an input, you might use this.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"example-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"example-input"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"email-error"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email-error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The email address is in an invalid format.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, on form submit, the text "The email address is in an invalid format." is added dynamically to the div. When the input is focused, this message will be read aloud to screen readers.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;aria-live&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;ARIA-live lets the computer know that an area of the page will be updated later. This is really handy with AJAX stuff. It can have a value of polite, aggressive, or off.&lt;/p&gt;

&lt;h3&gt;
  
  
  ARIA means extra context
&lt;/h3&gt;

&lt;p&gt;Using these attributes, you are giving the browser extra context so it can have a better idea of what functionality a certain element may have and more context to users of screen readers and other assistive technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Principles: A Refresher
&lt;/h2&gt;

&lt;p&gt;That wraps up my four simple principles.&lt;/p&gt;

&lt;p&gt;Just to recap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Principle 1 is that web design is more than graphic design.&lt;/li&gt;
&lt;li&gt;Principle 2 is be as semantic as possible.&lt;/li&gt;
&lt;li&gt;Principle 3 is that websites should look good naked.&lt;/li&gt;
&lt;li&gt;Principle 4 is talk to your computer, use ARIA.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By minding these principles, you will be able to avoid a good chunk of the mistakes that are made by using non-semantic code concerned only with appearances.&lt;/p&gt;

&lt;p&gt;And, if you want to see how to start putting these into practice, I'm launching a free email course: &lt;em&gt;&lt;a href="http://eepurl.com/dBGdPv" rel="noopener noreferrer"&gt;Common Website Accessibility Mistakes and How to Fix Them&lt;/a&gt;&lt;/em&gt;. Get access to the course by &lt;a href="http://eepurl.com/dBGdPv" rel="noopener noreferrer"&gt;signing up here&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>devtips</category>
      <category>html</category>
    </item>
    <item>
      <title>Lazy Loading Video Based on Connection Speed</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Fri, 01 Dec 2017 14:00:00 +0000</pubDate>
      <link>https://dev.to/benrobertson/lazy-loading-video-based-on-connection-speed-14co</link>
      <guid>https://dev.to/benrobertson/lazy-loading-video-based-on-connection-speed-14co</guid>
      <description>&lt;p&gt;A while back I was involved in redesigning the &lt;a href="https://www.upandup.agency" rel="noopener noreferrer"&gt;Up&amp;amp;Up&lt;/a&gt; website from the ground up. The team wanted to do one of those full-screen background videos on the home page. I wasn’t excited about it because the home page was already a little unwieldy with the large background images and some of the scroll-jacky things we are doing there.&lt;/p&gt;

&lt;p&gt;In past versions of the site, we’ve had some very heavy videos in the background. (I’m embarrassed to say, there was once a day we had a 40mb background video.) For this version, I wanted to make the site as performant as possible, so I got curious about how I could treat the background video as a progressive enhancement for users on connections that could handle a potentially large download. I made sure to emphasize to our team the importance of a small, compressed video file, but I also wanted some programmatic magic to happen too.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of the solution I ended up with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Try loading the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; with JavaScript&lt;/li&gt;
&lt;li&gt;Listen for the &lt;code&gt;canplaythrough&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Promise.race()&lt;/code&gt; to timeout the source loading if the &lt;code&gt;canplaythrough&lt;/code&gt; event doesn’t fire within 2 seconds.&lt;/li&gt;
&lt;li&gt;Remove the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; and cancel the video loading if we don’t detect the &lt;code&gt;canplaythrough&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;Fade the video in if we do detect the &lt;code&gt;canplaythrough&lt;/code&gt; event.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Markup
&lt;/h2&gt;

&lt;p&gt;The main thing to note in my video markup is that even though I am using the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements inside the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;, I have not set the &lt;code&gt;src&lt;/code&gt; attribute for either of the sources. If you set the &lt;code&gt;src&lt;/code&gt; attribute, the browser automatically finds the first &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; it can play and immediately starts downloading it.&lt;/p&gt;

&lt;p&gt;Since the video is a progressive enhancement in this example, we don’t need or want the video to load by default. In fact, the only thing that will load is the poster, which I have set to be the featured image of the page.&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;video class="js-video-loader video-autoplay--reduce-motion" poster="&amp;lt;?= $poster; ?&amp;gt;" muted="true" loop="true"&amp;gt;
    &amp;lt;source data-src="path/to/upup_homepage_loop.webm" type="video/webm"&amp;gt;
    &amp;lt;source data-src="path/to/upup_homepage_loop.mp4" type="video/mp4"&amp;gt;
&amp;lt;/video&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The JavaScript
&lt;/h2&gt;

&lt;p&gt;I wrote a small JavaScript class that looks for any video that has a &lt;code&gt;.js-video-loader&lt;/code&gt; class on it so that we could reuse this logic in the future for other videos.&lt;/p&gt;

&lt;p&gt;Here’s the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor () {
    this.videos = Array.from(document.querySelectorAll('video.js-video-loader'));

    if(!this.videos || window.innerWidth &amp;lt; 992) return;

    this.videos.forEach(this.loadVideo.bind(this));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we are doing in here is finding all the videos on the page that we want to lazy load. If there are none, we can return. The video was also having conflicts with some of the more intense scroll-jacking stuff we were doing on the homepage, so I’m also returning for small screens. (I’m thinking now I could have done this with media queries in the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements, but I’m not sure.)&lt;/p&gt;

&lt;p&gt;Then I run our video loading logic for each video.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;loadVideo()&lt;/code&gt; is a small function that calls some other functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadVideo(video) {
    this.setSource(video);

    // Reload the video with the new sources added.
    video.load();

    this.checkLoadTime(video);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setSource()&lt;/code&gt; is where we find the sources that we included as data attributes and add them as proper &lt;code&gt;src&lt;/code&gt; attributes.&lt;br&gt;
&lt;br&gt;
 &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Find the children of the video that are &amp;lt;source&amp;gt; tags.
 * Set the src attribute for each &amp;lt;source&amp;gt; based on the
 * data-src attribute.
 *
 * @param {DOM Object} video
 */

setSource (video) {
    let children = Array.from(video.children);
    children.forEach(child =&amp;gt; {
        if (
            child.tagName === 'SOURCE' &amp;amp;&amp;amp;
            typeof child.dataset.src !== 'undefined'
        ) {
            child.setAttribute('src', child.dataset.src);
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Basically what I am doing is looping through each child of the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element. I only want to find children that are &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements and that have a &lt;code&gt;data-src&lt;/code&gt; attribute defined (&lt;code&gt;child.dataset.src&lt;/code&gt;). If both of those conditions are met, we use &lt;code&gt;setAttribute&lt;/code&gt; to set the &lt;code&gt;src&lt;/code&gt; attribute of the source.&lt;/p&gt;

&lt;p&gt;Now that video element has its sources set, we need to tell the browser to try loading the video again. We did this above in our &lt;code&gt;loadVideo()&lt;/code&gt; function, with &lt;code&gt;video.load()&lt;/code&gt;. &lt;code&gt;load()&lt;/code&gt; is part of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement" rel="noopener noreferrer"&gt;HTMLMediaElement API&lt;/a&gt; that resets the media element and restarts the loading process.&lt;/p&gt;

&lt;p&gt;Next up is where the magic happens. In &lt;code&gt;checkLoadTime()&lt;/code&gt; I create two Promises. The first Promise resolves when the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element fires the &lt;a href="https://developer.mozilla.org/ro/docs/Web/Events/canplaythrough" rel="noopener noreferrer"&gt;canplaythrough&lt;/a&gt; event. This event is fired when the browser thinks it can play the media without stopping to buffer. To do this, we add an event listener in the Promise, and &lt;code&gt;resolve()&lt;/code&gt; only if the event is triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create a promise that resolves when the
// video.canplaythrough event triggers.
let videoLoad = new Promise((resolve) =&amp;gt; {
    video.addEventListener('canplaythrough', () =&amp;gt; {
        resolve('can play');
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also create another promise that functions as a timer. Inside the Promise, we use &lt;code&gt;setTimeout&lt;/code&gt; to resolve the Promise after an arbitrary time limit. For my purposes, I set a timeout of 2 seconds (2000 milliseconds).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create a promise that resolves after a
// predetermined time (2sec)
let videoTimeout = new Promise((resolve) =&amp;gt; {
    setTimeout(() =&amp;gt; {
        resolve('The video timed out.');
    }, 2000);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have two Promises, we can race them against each other to find out which one finishes first. &lt;code&gt;Promise.race()&lt;/code&gt; accepts an array of promises and we pass in the promises we created above to this function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Race the promises to see which one resolves first.
Promise.race([videoLoad, videoTimeout])
.then(data =&amp;gt; {
    if (data === 'can play') {
        video.play();
        setTimeout(() =&amp;gt; {
            video.classList.add('video-loaded');
        }, 3000);
    } else {
        this.cancelLoad(video);
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;.then()&lt;/code&gt; we are looking to receive the data from the Promise that resolves first. I send the string ‘can play’ through if the video can play, so I am checking against that to see if we can play the video. &lt;code&gt;video.play()&lt;/code&gt; uses the HTMLMediaElement &lt;code&gt;play()&lt;/code&gt; function to trigger the video to play.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;setTimeout()&lt;/code&gt; function adds the `.video-loaded’ class after 3 seconds to help the finesse the fade-in animation and the autoplay loop.&lt;/p&gt;

&lt;p&gt;If we don’t receive the &lt;code&gt;can play&lt;/code&gt; string, then we want to cancel the loading of the video.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;cancelLoad()&lt;/code&gt; method basically does the opposite of our &lt;code&gt;loadVideo()&lt;/code&gt; function. It removes the &lt;code&gt;src&lt;/code&gt; attribute from each &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; and then triggers &lt;code&gt;video.load()&lt;/code&gt; to reset the video element.&lt;/p&gt;

&lt;p&gt;If we didn’t do this, the video would keep loading in the background even though we aren’t displaying it.&lt;br&gt;
`&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Cancel the video loading by removing all
// source tags and then triggering video.load().
cancelLoad (video) {
    let children = Array.from(video.children);
    children.forEach(child =&amp;gt; {
        if (
            child.tagName === 'SOURCE' &amp;amp;&amp;amp;
            typeof child.dataset.src !== 'undefined'
        ) {
            child.parentNode.removeChild(child);
        }
    });

    // reload the video without source tags so it
    // stops downloading.
    video.load();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downfall of this method is that we are still attempting to download a potentially large file over a potentially poor connection, but by providing a timeout, I’m hoping to save data and recoup some performance for users on slow connections. In my tests in the Chrome Dev Tools throttled down to a Slow 3G connection, this logic ends up loading &amp;lt;512kb of the video before the timeout fires. Our current video loop is 3-5mb depending on if you are loading the webm or mp4, so this is still a significant savings for users on a slow connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;I’m pretty new to working with Promises, so this logic could probably be better, and I’m sure there’s things I’m doing with &lt;code&gt;resolve()&lt;/code&gt; (and not doing with &lt;code&gt;reject()&lt;/code&gt;) that I should or could be doing.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Write Accessible JavaScript</title>
      <dc:creator>Ben Robertson</dc:creator>
      <pubDate>Thu, 05 Oct 2017 12:00:00 +0000</pubDate>
      <link>https://dev.to/benrobertson/how-to-write-accessible-javascript-4ga9</link>
      <guid>https://dev.to/benrobertson/how-to-write-accessible-javascript-4ga9</guid>
      <description>&lt;p&gt;You’re wrapping up a web project and just as you’re putting the finishing touches you get sent a list of accessibility errors forwarded to you by your project manager. &lt;/p&gt;

&lt;p&gt;Inadequate color contrast. &lt;/p&gt;

&lt;p&gt;Missing alt tags. &lt;/p&gt;

&lt;p&gt;This interactive component needs to be keyboard accessible.&lt;/p&gt;

&lt;p&gt;We might not like to admit it but we’ve all been there: at the end of a project trying to reverse engineer accessibility into our site. It’s frustrating for developers, designers, project managers and clients.&lt;/p&gt;

&lt;p&gt;While accessibility can be frustrating, you can set yourself, your team, and your client up for success by planning for accessibility from the beginning. Here are 4 techniques to save you time and trouble when building accessible JavaScript-enabled websites and applications.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element for anything that users click on.&lt;/li&gt;
&lt;li&gt;Plan for Common Keyboard interactions.&lt;/li&gt;
&lt;li&gt;Manage ARIA states&lt;/li&gt;
&lt;li&gt;Managing Focus&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Use the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element for anything that users click on.
&lt;/h2&gt;

&lt;p&gt;In general, using semantic HTML elements will be a boon to the accessibility of your web project. When working with interactivity, the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; is &lt;em&gt;the&lt;/em&gt; semantic tag for things users are clicking on that aren’t links or other inputs. It is a semantic way to denote that an element is clickable and will be your new best friend.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The HTML &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element represents a clickable button.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;- &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button" rel="noopener noreferrer"&gt;Mozilla Developer Network&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you use the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element for interface elements that are clickable and bind click event listeners to those buttons, you get a lot of functionality for free.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;buttons are automatically focusable&lt;/strong&gt; ; they are in the tab index of a page. If a user lands on your site and is only using a keyboard, they can press the tab key to cycle through all the focusable elements, including hyperlinks and buttons, on your page.&lt;/p&gt;

&lt;p&gt;Second, screen readers will announce to a user that a button is in focus. &lt;strong&gt;Screen reader users know by default that button elements are interactive&lt;/strong&gt;. This makes it especially important to include clear, understandable text inside your &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; so all users can understand what clicking it will do. There are also some helpful &lt;code&gt;aria&lt;/code&gt; attributes you can add to your button, but we’ll &lt;a href="https://benrobertson.io/accessibility/javascript-accessibility#3-manage-aria-states" rel="noopener noreferrer"&gt;get to that later&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Third, when you add a click event listener to a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element, &lt;strong&gt;you get keyboard accessibility for free.&lt;/strong&gt; This means can write less JavaScript when you use the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element. By contrast, if you add a click event listener to a &lt;code&gt;div&lt;/code&gt;, you would also have to add keyup listeners for the spacebar and enter keys in order to make that element accessible to keyboards. With the button element, the default keyboard actions (spacebar and enter) and screen reader click actions trigger the click event. You don’t have to write extra keyboard listeners.&lt;/p&gt;

&lt;p&gt;To sum up: if a user is clicking on it and it’s not a link or some kind of input, just use a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Plan for Common Keyboard interactions.
&lt;/h2&gt;

&lt;p&gt;For more complex interactive components, there are likely several interactive elements in the component. Pay attention to what kind of event listeners you are adding to the DOM, and consider whether these actions be able to be triggered by the keyboard.&lt;/p&gt;

&lt;p&gt;For instance, is there a close or minimize button on your component? The ESC key should probably be able to trigger the close as well. Is there some sort of horizontal scroll-type action or Next / Previous buttons? Consider binding events to the arrow keys.&lt;/p&gt;

&lt;p&gt;Common interactions can include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Exiting the current component&lt;/li&gt;
&lt;li&gt;Submitting&lt;/li&gt;
&lt;li&gt;Moving position / browsing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Common keys to add actions to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enter (keyCode 13)&lt;/li&gt;
&lt;li&gt;spacebar (keyCode 32&lt;/li&gt;
&lt;li&gt;arrow keys (37 - 40)&lt;/li&gt;
&lt;li&gt;ESC (keyCode 27)&lt;/li&gt;
&lt;li&gt;tab (keyCode 9)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How do you bind actions to specific keys? You can do it by adding an event listener to the &lt;code&gt;keyup&lt;/code&gt; event. When you pass the event into your callback function, you have access to the &lt;code&gt;keyCode&lt;/code&gt; property, and you can trigger actions depending on the keyCode. I have a hard time remember the &lt;code&gt;keyCodes&lt;/code&gt;, so often during development I’ll add an event listener that logs all keyCodes to the console so that I can find the ones I need to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keyup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make things a little bit easier though, I’ll document the most common keycodes I end up needing to reference. Here is a common pattern I end up using in components, with the most common keyCodes that I use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keyup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// escape&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// exit&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// enter || spacebar&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// submit or something&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// left arrow&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// move back / previous&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// right arrow&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// move forward&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// up arrow&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// move up&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// down arrow&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// move down&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t use all of these in every situation, but they are the ones I use most frequently.&lt;/p&gt;

&lt;p&gt;Something else you’ll want to keep in mind is that you’ll often want to add these event listeners conditionally: only when a certain component is active or in use by the user. Otherwise you may have actions being triggered at the wrong time if all of your event listeners are in the global scope.&lt;/p&gt;

&lt;p&gt;To add these conditionally, I usually have a function that handles all the keyup logic (with the creative name &lt;code&gt;this.handleKeyup()&lt;/code&gt;). When my component is activated, I add an event listener with that function as the callback. When my component is disabled, I fire a &lt;code&gt;removeEventListener()&lt;/code&gt; with that same function as the callback. That way, you can trigger different actions with different keys depending on what the user is doing at the moment.&lt;/p&gt;

&lt;p&gt;You can take this to another level and test whether the user was also holding down the shift key by testing if &lt;code&gt;event.shiftKey === true&lt;/code&gt;. You might do this is you are trying to trap focus inside of a modal and want to prevent users from &lt;code&gt;SHIFT+TAB&lt;/code&gt;ing backwards out of a modal.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Manage ARIA states
&lt;/h2&gt;

&lt;p&gt;There’s a lot to the &lt;a href="https://www.w3.org/WAI/intro/aria" rel="noopener noreferrer"&gt;Web Accessibility Initiative’s Accessibility of Rich Internet Applications&lt;/a&gt; (WAI-ARIA, or just ARIA) spec, but when you’re getting started with interactive JavaScript you should really focus on the &lt;code&gt;aria-expanded&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;A lot of interactivity is focused on showing or hiding content on the page. The &lt;code&gt;aria-expanded&lt;/code&gt; property “indicates whether the element, or another grouping element it controls, is currently expanded or collapsed,” according to the &lt;a href="https://www.w3.org/TR/wai-aria/states_and_properties#aria-expanded" rel="noopener noreferrer"&gt;W3C spec&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You’ll want to make sure that your element renders with the appropriate &lt;code&gt;aria-expanded&lt;/code&gt; attribute: false if the element is not expanded, true if the element is expanded. This attribute should be applied to the element that controls the expandable element. If the grouping element is a child of the controlling element, you don’t need to do anything special, but if you have a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; that is going to control a sibling &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;, you will need to indicate that that the button controls the list with the &lt;code&gt;aria-controls&lt;/code&gt; attribute (&lt;a href="https://www.w3.org/TR/wai-aria/states_and_properties#aria-controls" rel="noopener noreferrer"&gt;aria-controls documentation at W3C&lt;/a&gt;). This attribute accepts an ID or list of IDs that are controlled by the interactive element. In our example, our markup would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-expander"&lt;/span&gt; &lt;span class="na"&gt;aria-expanded=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;aria-controls=&lt;/span&gt;&lt;span class="s"&gt;"expandable-list-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Expand List&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"expandable-list-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://example.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sample Link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://example.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sample Link 2&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://example.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sample Link 3&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to toggle the expanded state. The way I normally do this is with the &lt;code&gt;setAttribute()&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listExpander&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.list-expander&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#expandable-list-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;listExpander&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that when I check to see the value of the &lt;code&gt;aria-expanded&lt;/code&gt; attribute, I use &lt;code&gt;=== "true"&lt;/code&gt;. That’s because &lt;code&gt;getAttribute&lt;/code&gt; returns either the string &lt;code&gt;"true"&lt;/code&gt; or &lt;code&gt;"false"&lt;/code&gt;, not an actual true or false value. (That tripped me up at first).&lt;/p&gt;

&lt;p&gt;You can use this same kind of thinking with other true / false ARIA attributes. Most commonly, I use this with &lt;code&gt;aria-hidden&lt;/code&gt; for showing and hiding modal dialogs.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Managing Focus
&lt;/h2&gt;

&lt;p&gt;The last thing we’ll cover in this guide is managing focus. Focus refers to the singular element in the browser that is able to be acted upon via the keyboard. Elements often receive focus when a user clicks on them, uses the TAB key to cycle through focusable elements, or uses a screen reader. At a basic level, you need to make sure that users can visually tell at any time what element is in focus.&lt;/p&gt;

&lt;p&gt;The most common place that I end up managing focus is in modal components.&lt;/p&gt;

&lt;p&gt;Here’s a sample problem we need to solve. We have an about page that contains a bio of a person and a button that says “Contact this person”. This button opens a modal that contains a contact form. But if the form is not in the natural tab order of the page (as is common with modals), when the user hits tab their keyboard focus is behind the modal. It’s common for keyboard and assistive technology users to get stuck and frustrated with poorly designed modals.&lt;/p&gt;

&lt;p&gt;To solve this, we want to do a couple of things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When the modal opens, move the focus to the first focusable element inside the modal.&lt;/li&gt;
&lt;li&gt;Ensure that users can easily close the modal via the keyboard when it is open.&lt;/li&gt;
&lt;li&gt;When the modal closes, return focus to the element that was active when the modal opened.&lt;/li&gt;
&lt;li&gt;If we want to be really careful, we can trap the TAB forward and backwards inside the modal so users can’t escape unless they close the modal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Get the first focusable element.
&lt;/h3&gt;

&lt;p&gt;I have a few helper methods to help me determine all focusable elements and the first focusable element in a given context. Here’s how I find all focusable elements on the page (h/t to &lt;a href="https://gomakethings.com/how-to-get-the-first-and-last-focusable-elements-in-the-dom/" rel="noopener noreferrer"&gt;Chris Ferdinandi&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Get all focusable elements inside of the specifed context.
 *
 * @param {String} [context='document'] The DOM context you want to search in.
 * @return {Array} Array of focusable elements
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFocusable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button, [href], select, textarea, input:not([type="hidden"]), [tabindex]:not([tabindex="-1"])&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function uses &lt;code&gt;querySelectorAll&lt;/code&gt; with a list of selectors that are normally focusable: &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, links with an &lt;code&gt;href&lt;/code&gt; attribute, inputs, and things that have a tabindex set (that is not -1). I also am filtering the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; selector by removing any input that is hidden, since those aren’t focusable. I do the same kind of filtering for elements with a &lt;code&gt;tabindex&lt;/code&gt; attribute set to -1, since those elements should only be focusable via JavaScript method, not in the normal tab index. I use &lt;code&gt;Array.from&lt;/code&gt; to create an array from the NodeList returned by &lt;code&gt;querySelectorAll&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What I like about this function is that I can also pass in a context. By default, the context is set to &lt;code&gt;document&lt;/code&gt;, so it will find all focusable elements in the document. But in our modal example above, you could pass in the modal element itself as the context and get a list of all focusable elements in the modal 😎.&lt;/p&gt;

&lt;p&gt;Finding the first focusable element is trivial now, it’s a matter of popping off the first element in our array. I typically have another helper function to get me the first focusable element, and don’t actually call that first one directly. It is like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Get the first focusable element inside of the specified context.
 *
 * @param {String} [context='document'] The DOM context you want to search in.
 * @return {Object} A DOM element
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFirstFocusable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFocusable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You pass in a context and it calls our original &lt;code&gt;getFocusable()&lt;/code&gt; function and returns the first item in the array. Now we can call &lt;code&gt;focus()&lt;/code&gt; on that element to programmatically focus the first focusable element. It would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;getFirstFocusable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ensure that users can easily close the modal via the keyboard when it is open
&lt;/h3&gt;

&lt;p&gt;We’ve partially addressed this earlier when we discussed planning for common keyboard interactions. This is a perfect example of time when you want the user to be able to ESC out of a component.&lt;/p&gt;

&lt;p&gt;You might also add an overlay between the modal and the site content that is clickable and focusable with click events that close the modal.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the modal closes, return focus to the element that was active when the modal opened.
&lt;/h3&gt;

&lt;p&gt;In our example, the user clicked on a button and then their focus jumped to the modal. When they close the modal, we want to return their focus to the button that triggered the modal. This is actually pretty trivial using the &lt;code&gt;document.activeElement&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;When we detect that a modal should open and &lt;strong&gt;before&lt;/strong&gt; we transfer the focus to that modal, we can save the current active element to a variable like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;previousActiveElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can transfer focus to our first focusable element, and whenever a user is done with the modal and decides to close it we transfer the focus back to our saved element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;previousActiveElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the user is back where they started!&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap the TAB and SHIFT + TAB inside the modal
&lt;/h3&gt;

&lt;p&gt;As I mentioned above, if we want to be really careful, we can trap the TAB forward and backwards inside the modal so users can’t escape unless they close the modal.&lt;/p&gt;

&lt;p&gt;To do this, we need to listen to the &lt;code&gt;keyup&lt;/code&gt; event while the modal is active, and here’s the function I use to trap the focus (it depends on our &lt;code&gt;getFocusable()&lt;/code&gt; function from above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Traps the tab key inside of the context, so the user can't accidentally get
 * stuck behind it.
 *
 * Note that this does not work for VoiceOver users who are navigating with
 * the VoiceOver commands, only for default tab actions. We would need to
 * implement something like the inert attribute for that (see https://github.com/WICG/inert)
 * @param {object} e the Event object
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;trapTabKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;focusableItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFocusable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;focusedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;focusedItemIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusableItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;focusedItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;focusedItemIndex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;focusableItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusableItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;focusedItemIndex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;focusableItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;focusableItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we need to pass in the event object so we can detect what key is being pressed and a context for the user to be “trapped” inside of.&lt;/p&gt;

&lt;p&gt;If the key they pressed was &lt;strong&gt;not&lt;/strong&gt; the TAB key, we can safely return and do nothing.&lt;/p&gt;

&lt;p&gt;If it &lt;strong&gt;was&lt;/strong&gt; the TAB key, we get all the focusable elements in the modal and the element they are currently focused on. Once we have these two things, we can use the &lt;code&gt;indexOf&lt;/code&gt; method to tell where the user is in the tab order of this context.&lt;/p&gt;

&lt;p&gt;If they were holding the shift key (&lt;code&gt;e.shiftKey === true&lt;/code&gt;), they were going backwards, so we want to stop them when they get to the first focusable item in the modal and focus on the last focusable item: &lt;code&gt;focusableItems[focusableItems.length - 1].focus()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If they were going forward and got to the last focusable item in the modal (&lt;code&gt;focusedItemIndex == focusableItems.length - 1&lt;/code&gt;), we need to focus the first focusable item.&lt;/p&gt;

&lt;p&gt;We need to call &lt;code&gt;e.preventDefault()&lt;/code&gt; for both of these cases to prevent the default TAB function from firing. For all other instances though, we can let them TAB normally.&lt;/p&gt;

&lt;p&gt;You’ll want to make sure you remove your &lt;code&gt;keyup&lt;/code&gt; event listener when the user closes the modal to let their TAB functionality return to normal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We’ve covered a lot here, but it should be a really good start for you to start developing accessible interactive JavaScript sites and applications and give you a framework for thinking about how you might program other widgets and components. Remember to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; for clickable elements&lt;/li&gt;
&lt;li&gt;Plan for common keyboard interactions like ESC, Arrows, Enter and TAB.&lt;/li&gt;
&lt;li&gt;Think about and manage any appropriate ARIA states.&lt;/li&gt;
&lt;li&gt;Manage focus when necessary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping these techniques in mind from the beginning will save you time and trouble and your users will thank you!&lt;/p&gt;

&lt;p&gt;PS: If you want even more accessibility tips, I'm launching a free email course: &lt;a href="https://benjamingrobertson.us15.list-manage.com/subscribe?u=aafc0f8e65dbc564446043b15&amp;amp;id=baaa94d97b" rel="noopener noreferrer"&gt;Common Website Accessibility Mistakes and How to Fix Them&lt;/a&gt;. Get access to the course by &lt;a href="https://benjamingrobertson.us15.list-manage.com/subscribe?u=aafc0f8e65dbc564446043b15&amp;amp;id=baaa94d97b" rel="noopener noreferrer"&gt;signing up here&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>a11y</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
