<?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: Priya Nair</title>
    <description>The latest articles on DEV Community by Priya Nair (@priya_nair).</description>
    <link>https://dev.to/priya_nair</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%2F3868481%2Fb1f50609-3fa2-4d3a-a172-285aa7c95374.png</url>
      <title>DEV Community: Priya Nair</title>
      <link>https://dev.to/priya_nair</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/priya_nair"/>
    <language>en</language>
    <item>
      <title>ARIA Labels Done Wrong: The Most Common Mistakes I See in Production Code</title>
      <dc:creator>Priya Nair</dc:creator>
      <pubDate>Wed, 08 Apr 2026 20:19:59 +0000</pubDate>
      <link>https://dev.to/priya_nair/aria-labels-done-wrong-the-most-common-mistakes-i-see-in-production-code-1943</link>
      <guid>https://dev.to/priya_nair/aria-labels-done-wrong-the-most-common-mistakes-i-see-in-production-code-1943</guid>
      <description>&lt;h1&gt;
  
  
  ARIA Labels Done Wrong: The Most Common Mistakes I See in Production Code
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Meta: ARIA is powerful but dangerous when misused. Here are the 7 mistakes I see constantly—and how to fix them.&lt;/p&gt;

&lt;p&gt;Keyword: ARIA labels accessibility mistakes common&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Tags: #accessibility #aria #wcag #webdev #a11y&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Golden Rule of ARIA (And Why Most People Break It)
&lt;/h2&gt;

&lt;p&gt;Before we talk about mistakes, let me say this clearly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No ARIA is better than bad ARIA.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ARIA (Accessible Rich Internet Applications) is a powerful toolset for filling accessibility gaps. But when you use it wrong—and I mean &lt;em&gt;really wrong&lt;/em&gt;—it makes things worse for screen reader users, not better.&lt;/p&gt;

&lt;p&gt;I've audited hundreds of codebases. I'd estimate 70% of the ARIA I see is either unnecessary or broken. And when a screen reader user encounters broken ARIA, they don't get a degraded experience. They get a &lt;em&gt;confusing&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;So here's the deal: I'm going to walk you through the most common mistakes I see, why they're problems, and how to fix them. By the end, you'll know when to use ARIA and when to just use HTML.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 1: aria-label on a Div Instead of Using Semantic HTML
&lt;/h2&gt;

&lt;p&gt;This is the #1 mistake. By far.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern:&lt;/strong&gt; Someone needs a button. Instead of using &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, they use a styled &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and add &lt;code&gt;aria-label&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it's wrong:&lt;/strong&gt; A &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; is invisible to screen readers and keyboard navigation. No amount of ARIA can change that. You're building an accessibility shim on top of an inaccessible foundation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ This is broken, even with aria-label --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
  &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Submit form"&lt;/span&gt; 
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
  &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"submitForm()"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Submit
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- What a screen reader hears: "Submit" (as text, not a button) --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- What keyboard users can do: Nothing (not focusable) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ Use semantic HTML --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"submitForm()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR if you need a link styled as a button: --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/submit"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Submit, button" --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Keyboard: Focusable with Tab, activatable with Enter --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Screen reader users need to know it's a button. Keyboard users need to be able to reach it with Tab and activate it with Enter or Space. A bare &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; does neither. ARIA's &lt;code&gt;role="button"&lt;/code&gt; &lt;em&gt;partially&lt;/em&gt; helps, but you still need to add keyboard listeners manually:&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="c1"&gt;// If you insist on using a div (you shouldn't):&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakeButton&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;[role="button"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;fakeButton&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;keydown&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;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;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&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; &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="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;fakeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&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;&lt;strong&gt;One-liner:&lt;/strong&gt; Use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; before you even think about &lt;code&gt;aria-label&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 2: aria-label Duplicating Visible Text
&lt;/h2&gt;

&lt;p&gt;This one wastes screen reader users' time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern:&lt;/strong&gt; You have a button with visible text. You add &lt;code&gt;aria-label&lt;/code&gt; with the same text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ Redundant --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Click to download file"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Download
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Click to download file, button" (verbose, redundant) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it's wrong:&lt;/strong&gt; If the button text is clear, you don't need &lt;code&gt;aria-label&lt;/code&gt;. Screen readers already announce the text. You're just making them say it twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ Let the text speak for itself --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Download&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR, if you need to clarify: --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Download Invoice PDF&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Download Invoice PDF, button" (clear, not redundant) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When aria-label IS necessary:&lt;/strong&gt; When you have an icon-only button (see Mistake 3).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-liner:&lt;/strong&gt; Don't use &lt;code&gt;aria-label&lt;/code&gt; to repeat visible text. Use it to &lt;em&gt;clarify&lt;/em&gt; invisible intent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 3: Icon-Only Buttons Without aria-label (Or ARIA)
&lt;/h2&gt;

&lt;p&gt;This is the flip side of Mistake 2. You have a button with &lt;em&gt;only&lt;/em&gt; an icon. No text. No ARIA. Screen readers have no idea what it does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ Icon-only, no label --&amp;gt;&lt;/span&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;"close-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon-x"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Button" (what button? who knows?) --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ❌ Or worse: --&amp;gt;&lt;/span&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;"hamburger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Button" (again, meaningless) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ Icon + aria-label --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close dialog"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"close-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon-x"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Close dialog, button" (clear) --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ Icon + aria-label + title (tooltip bonus) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Open navigation menu"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hamburger"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;line&lt;/span&gt; &lt;span class="na"&gt;x1=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;y1=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="na"&gt;x2=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;y2=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Open navigation menu, button" --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Hover tooltip: "Menu" --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real-world checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is the button icon-only (no text)? If yes, add &lt;code&gt;aria-label&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] Does the &lt;code&gt;aria-label&lt;/code&gt; describe what happens? If yes, you're good.&lt;/li&gt;
&lt;li&gt;[ ] Do sighted users also understand? (Can't rely on icon alone.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;One-liner:&lt;/strong&gt; Every icon-only button needs &lt;code&gt;aria-label&lt;/code&gt;, &lt;code&gt;aria-labelledby&lt;/code&gt;, or text inside the button.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 4: aria-labelledby Pointing to an ID That Doesn't Exist
&lt;/h2&gt;

&lt;p&gt;This is a silent killer. No error. No warning. Just broken accessibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ ID doesn't exist --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"form-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Account Settings&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Later... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"nonexistent-id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="c"&gt;&amp;lt;!-- Wrong ID! --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&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="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Form" (no context) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ ID exists and is used correctly --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"form-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Account Settings&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"form-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="c"&gt;&amp;lt;!-- Correct ID --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&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="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Account Settings, form" (context clear) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to debug:&lt;/strong&gt; Open Chrome DevTools → Accessibility panel. It'll warn you if an ID is broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-liner:&lt;/strong&gt; Always check that the ID in &lt;code&gt;aria-labelledby&lt;/code&gt; actually exists in your HTML.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 5: role="button" on a Div Without Keyboard Handling
&lt;/h2&gt;

&lt;p&gt;This is the classic "I tried to fix it but didn't finish" pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ Has role, but no keyboard support --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"doSomething()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Click me
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: "Click me, button" (sounds accessible) --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Keyboard user: Tabs right past it (not focusable) --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ Full keyboard support --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
  &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; 
  &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="na"&gt;--&lt;/span&gt; &lt;span class="na"&gt;Make&lt;/span&gt; &lt;span class="na"&gt;it&lt;/span&gt; &lt;span class="na"&gt;focusable&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  onclick="doSomething()"
  onkeydown="handleKeydown(event)"  &lt;span class="c"&gt;&amp;lt;!-- Handle keyboard --&amp;gt;&lt;/span&gt;
&amp;gt;
  Click me
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleKeydown&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="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;event&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;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&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;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; &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="nf"&gt;preventDefault&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR, even better: --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"doSomething()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The real talk:&lt;/strong&gt; If you're adding &lt;code&gt;role="button"&lt;/code&gt;, you're probably doing something wrong. You should be using a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. &lt;code&gt;role="button"&lt;/code&gt; is for when you have a &lt;em&gt;very good reason&lt;/em&gt; to not use HTML buttons (like custom styling that breaks them, which is rare).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-liner:&lt;/strong&gt; If you use &lt;code&gt;role="button"&lt;/code&gt;, you must also add &lt;code&gt;tabindex="0"&lt;/code&gt; and keyboard event listeners. (But really, just use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 6: Hiding Content With aria-hidden="true" That Users Can Still Reach
&lt;/h2&gt;

&lt;p&gt;This one creates a horrible experience: keyboard users can reach an element, but screen readers can't see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ aria-hidden but still focusable --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"doSomething()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Hidden Action
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Screen reader: Ignores the button entirely --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Keyboard user: Tabs to it, tries to interact, gets confused --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When this happens:&lt;/strong&gt; Usually with decorative elements that accidentally got tabindex, or off-canvas menus that aren't fully hidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ Truly hidden: not visible, not focusable, not announced --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
  &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: none;"&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="na"&gt;--&lt;/span&gt; &lt;span class="na"&gt;Remove&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt; &lt;span class="na"&gt;document&lt;/span&gt; &lt;span class="na"&gt;flow&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&amp;gt;
  Decorative icon
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ OR: Off-canvas menu that's actually invisible --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; 
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position: fixed; left: -300px;"&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="na"&gt;--&lt;/span&gt; &lt;span class="na"&gt;Off-screen&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  aria-hidden="true"
&amp;gt;
  Menu items
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ OR: Use visibility + aria-hidden for keyboard access but screen reader hiding --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"visibility: hidden;"&lt;/span&gt;
  &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Invisible to all users
&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;&lt;strong&gt;Test:&lt;/strong&gt; Inspect the element in DevTools. Is it visible? Is it focusable? If both are true but &lt;code&gt;aria-hidden="true"&lt;/code&gt; is set, that's a problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-liner:&lt;/strong&gt; Only use &lt;code&gt;aria-hidden="true"&lt;/code&gt; on elements that are actually invisible/non-interactive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistake 7: Dynamic Content Updates Not Announced (Missing aria-live)
&lt;/h2&gt;

&lt;p&gt;A screen reader user is on your page. New content loads via JavaScript. The screen reader has no idea. User keeps reading old content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ❌ Content updates, but no announcement --&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;"notifications"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Initially empty --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// New notification arrives&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notification&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;createElement&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order #12345 shipped!&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Screen reader: *crickets* (no announcement)&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good example:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- ✅ Content updates AND is announced --&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;"notifications"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt; &lt;span class="na"&gt;aria-atomic=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Initially empty --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notification&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;createElement&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order #12345 shipped!&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Screen reader: "Order #12345 shipped!" (announced immediately)&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ Or, for urgent updates: --&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;"alerts"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"assertive"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Errors, warnings, urgent messages --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// This gets announced immediately, interrupting other content&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alerts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your session will expire in 2 minutes.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;aria-live options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-live="polite"&lt;/code&gt; — Announce when convenient (after current speech)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-live="assertive"&lt;/code&gt; — Announce immediately (interrupts current speech)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="alert"&lt;/code&gt; — Shorthand for &lt;code&gt;aria-live="assertive" aria-atomic="true"&lt;/code&gt; (errors, warnings)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="status"&lt;/code&gt; — Shorthand for &lt;code&gt;aria-live="polite" aria-atomic="true"&lt;/code&gt; (status updates)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-world examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form validation errors → use &lt;code&gt;role="alert"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"Saved successfully" message → use &lt;code&gt;role="status"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Real-time notifications → use &lt;code&gt;aria-live="polite"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;One-liner:&lt;/strong&gt; If content loads dynamically, wrap it in &lt;code&gt;aria-live&lt;/code&gt; so screen readers know it's there.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Test: Chrome DevTools
&lt;/h2&gt;

&lt;p&gt;Here's the fastest way to catch most ARIA mistakes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your site in Chrome.&lt;/li&gt;
&lt;li&gt;Open DevTools (F12).&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Elements&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Right-click any element → &lt;strong&gt;Inspect accessibility properties&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Look at the &lt;strong&gt;Computed Name&lt;/strong&gt; section.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What you're checking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the element have a name? (If it's interactive, it should.)&lt;/li&gt;
&lt;li&gt;Is the name what you intended? (Not "button" or blank.)&lt;/li&gt;
&lt;li&gt;Are there warnings? (Red warning icon = problem.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Element: &amp;lt;button&amp;gt;
Computed Name: "Submit"
Role: button
Warnings: None ✓

---

Element: &amp;lt;div role="button" aria-label="close"&amp;gt;
Computed Name: "close"
Role: button
Warnings: None ✓

---

Element: &amp;lt;button aria-label="nonexistent-id"&amp;gt;
Computed Name: (empty)
Role: button
Warnings: ⚠️ aria-labelledby points to non-existent element
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing With a Real Screen Reader
&lt;/h2&gt;

&lt;p&gt;If Chrome DevTools isn't enough, test with an actual screen reader.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Mac:&lt;/strong&gt; VoiceOver is built-in. Turn it on: System Preferences → Accessibility → VoiceOver. Press Ctrl+Option+U to start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Windows:&lt;/strong&gt; NVDA is free. Download from &lt;a href="https://www.nvaccess.org/" rel="noopener noreferrer"&gt;nvaccess.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listen for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the button announcement sound right? (Not just "button," but "submit button" or "close dialog"?)&lt;/li&gt;
&lt;li&gt;Can you navigate to all interactive elements?&lt;/li&gt;
&lt;li&gt;Do new notifications get announced?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It takes 10 minutes to learn the basics. Highly worth it.&lt;/p&gt;




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

&lt;p&gt;Running through a codebase? Use this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] No &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; without &lt;code&gt;tabindex="0"&lt;/code&gt; and keyboard handlers&lt;/li&gt;
&lt;li&gt;[ ] Every &lt;code&gt;aria-label&lt;/code&gt; / &lt;code&gt;aria-labelledby&lt;/code&gt; has a real target&lt;/li&gt;
&lt;li&gt;[ ] Icon-only buttons have &lt;code&gt;aria-label&lt;/code&gt; or text&lt;/li&gt;
&lt;li&gt;[ ] No &lt;code&gt;aria-hidden="true"&lt;/code&gt; on focusable elements&lt;/li&gt;
&lt;li&gt;[ ] Dynamic content has &lt;code&gt;aria-live&lt;/code&gt; or &lt;code&gt;role="alert"&lt;/code&gt; / &lt;code&gt;role="status"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] ARIA attributes only add information, not replace semantic HTML&lt;/li&gt;
&lt;li&gt;[ ] Form fields have associated &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ARIA Violations I See Most
&lt;/h2&gt;

&lt;p&gt;After auditing 200+ projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;aria-label duplicating text&lt;/strong&gt; (70% of aria-label misuse)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;role="button" without keyboard&lt;/strong&gt; (30% of role misuse)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aria-hidden hiding interactive content&lt;/strong&gt; (25% of aria-hidden issues)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing aria-live on dynamic updates&lt;/strong&gt; (50% of single-page apps)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aria-labelledby pointing to nothing&lt;/strong&gt; (15% of aria-labelledby)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Real Rule
&lt;/h2&gt;

&lt;p&gt;Here's how to think about ARIA:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ARIA is for semantics and live regions. Not for fixing broken HTML.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're tempted to use ARIA, ask yourself first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is there a semantic HTML element that does this? (Usually yes.)&lt;/li&gt;
&lt;li&gt;Is the content actually hidden/dynamic? (If not, probably doesn't need ARIA.)&lt;/li&gt;
&lt;li&gt;Are screen reader users getting accurate information? (Test it.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of the time, better HTML solves the problem faster than ARIA ever could.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources (All Free, Official)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MDN ARIA documentation&lt;/strong&gt; — &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA" rel="noopener noreferrer"&gt;developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAI-ARIA Authoring Practices Guide&lt;/strong&gt; — &lt;a href="https://www.w3.org/WAI/ARIA/apg/" rel="noopener noreferrer"&gt;w3.org/WAI/ARIA/apg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARIA in HTML Spec&lt;/strong&gt; — &lt;a href="https://www.w3.org/TR/html-aria/" rel="noopener noreferrer"&gt;w3.org/TR/html-aria&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't light reading, but they're authoritative. Bookmark them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Closing Truth
&lt;/h2&gt;

&lt;p&gt;ARIA is a powerful tool for bridging the gap between modern web apps and assistive technology. But it's a tool for &lt;em&gt;filling gaps&lt;/em&gt;, not building on nothing.&lt;/p&gt;

&lt;p&gt;Master semantic HTML first. Learn ARIA second. Test with actual screen readers third.&lt;/p&gt;

&lt;p&gt;When you do that, you'll stop making these mistakes. And your sites will work better for &lt;em&gt;everyone&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Priya Nair&lt;/strong&gt; is a frontend developer based in Amsterdam who is genuinely tired of seeing broken ARIA in production. She tests with screen readers regularly and believes that if a feature needs ARIA to work, you probably should have built it differently. When not debugging ARIA, she advocates for better HTML.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Audited My Own Portfolio for WCAG 2.2 — Here's What I Found in 30 Minutes</title>
      <dc:creator>Priya Nair</dc:creator>
      <pubDate>Wed, 08 Apr 2026 20:15:21 +0000</pubDate>
      <link>https://dev.to/priya_nair/i-audited-my-own-portfolio-for-wcag-22-heres-what-i-found-in-30-minutes-4e55</link>
      <guid>https://dev.to/priya_nair/i-audited-my-own-portfolio-for-wcag-22-heres-what-i-found-in-30-minutes-4e55</guid>
      <description>&lt;h1&gt;
  
  
  I Audited My Own Portfolio for WCAG 2.2 — Here's What I Found in 30 Minutes
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Meta: I audited my portfolio for WCAG 2.2 and found six critical accessibility issues I'd been ignoring. Here's how to find them in yours.&lt;/p&gt;

&lt;p&gt;Keyword: WCAG 2.2 audit checklist developer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Tags: #accessibility #webdev #wcag #frontend&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment I Realized I Was Part of the Problem
&lt;/h2&gt;

&lt;p&gt;Last week, I was reviewing accessibility best practices for a client project when it hit me: I haven't formally audited &lt;em&gt;my own&lt;/em&gt; portfolio in years. You know, the one I show to potential clients when I talk about how much I care about inclusive design.&lt;/p&gt;

&lt;p&gt;Oof.&lt;/p&gt;

&lt;p&gt;So I decided to do something radical—actually practice what I preach. I set a timer for 30 minutes, grabbed a couple of free tools, and started poking around. What I found was both humbling and, frankly, pretty typical. And that's exactly why I'm writing this.&lt;/p&gt;

&lt;p&gt;The web is broken for accessibility. The WebAIM Million 2025 report shows that &lt;strong&gt;95.9% of websites fail basic WCAG checks&lt;/strong&gt;. Ninety-five point nine percent. That's not bad luck. That's us. Developers. We did this. Usually without meaning to, usually while thinking about 47 other things, but we did it.&lt;/p&gt;

&lt;p&gt;Here's the good news: we can also fix it. And if the EU's Accessibility Act (EAA) being enforced as of June 2025 isn't motivation enough, maybe knowing that your actual users—whether they're blind, colorblind, using only a keyboard, or just on a tiny mobile screen in bad sunlight—will actually be able to use your site, is.&lt;/p&gt;

&lt;p&gt;Let me walk you through what I found, how I found it, and how to fix it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tools (All Free, All Good)
&lt;/h2&gt;

&lt;p&gt;Before we dig in, let me be clear: you don't need expensive software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;axe DevTools&lt;/strong&gt; (browser extension, free) — This is my workhorse. It runs automated accessibility scans and flags issues with severity and WCAG criteria. Chrome, Firefox, Edge. Get it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WAVE&lt;/strong&gt; (browser extension, free) — Shows you a visual representation of content structure, labels, and warnings. Slightly different angle than axe, so both are worth running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome DevTools — Accessibility Tree&lt;/strong&gt; (built in, free) — Under DevTools → Elements → Accessibility, you can see how your page is parsed by assistive technology. This is eye-opening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keyboard testing&lt;/strong&gt; (your keyboard, free) — Press Tab. See what happens. Try to navigate your entire page without touching the mouse. This matters more than any tool.&lt;/p&gt;

&lt;p&gt;That's it. Ten minutes to set up. Let's go.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Found: Six Things That Made Me Wince
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Missing and Terrible Alt Text on Images
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The issue:&lt;/strong&gt; I had a gorgeous hero image on my portfolio landing page. Beautiful. Completely undescribed.&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="c"&gt;&amp;lt;!-- ❌ What I had --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"portfolio-hero.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ What I should have --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"portfolio-hero.jpg"&lt;/span&gt; 
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Portfolio website displaying three web design projects with dark theme and gradient accents"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it means for real users:&lt;/strong&gt; Blind users get nothing. Screen readers say "image" and move on. Image broken or slow to load? Sighted users see the broken image icon. Blind users see nothing—they don't even know there was supposed to be content there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Write alt text that describes the &lt;em&gt;content and purpose&lt;/em&gt; of the image. Not poetic. Not &lt;code&gt;alt="beautiful sunset"&lt;/code&gt;. Useful. What does this image &lt;em&gt;do&lt;/em&gt; on your page? If it's decorative, use &lt;code&gt;alt=""&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Insufficient Color Contrast (The Sneaky One)
&lt;/h3&gt;

&lt;p&gt;My design had a really slick dark gray text on a slightly darker gray background. Very trendy. Very unreadable if you have low vision, color blindness, or are just squinting at your phone in daylight.&lt;/p&gt;

&lt;p&gt;axe flagged it: &lt;strong&gt;3.2:1 contrast ratio&lt;/strong&gt;. WCAG AA requires &lt;strong&gt;4.5:1&lt;/strong&gt; for normal text, &lt;strong&gt;3:1&lt;/strong&gt; for large text (18px+ or 14px bold+).&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="c"&gt;/* ❌ What I had (3.2:1 - FAIL) */&lt;/span&gt;
&lt;span class="nc"&gt;.post-date&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#666666&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="m"&gt;#1a1a1a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* ✅ What I fixed (4.8:1 - PASS) */&lt;/span&gt;
&lt;span class="nc"&gt;.post-date&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#cccccc&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="m"&gt;#1a1a1a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&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;&lt;strong&gt;Quick way to check:&lt;/strong&gt; Use the &lt;a href="https://webaim.org/resources/contrastchecker/" rel="noopener noreferrer"&gt;WebAIM contrast checker&lt;/a&gt; (free, no login). Paste your hex codes, see your ratio. Aim for 4.5:1 unless your text is genuinely big.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Focus Indicators (The Invisible Tab)
&lt;/h3&gt;

&lt;p&gt;I had removed the default browser focus outline on links because it "looked ugly." Instead, I'd added a subtle &lt;code&gt;:hover&lt;/code&gt; effect. Very pretty. Very broken if you only use keyboard.&lt;/p&gt;

&lt;p&gt;Someone using a keyboard to navigate my site would Tab onto a link and... see nothing change visually. Where am I? Did that work? Panic.&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="c"&gt;/* ❌ What I had */&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* THE SIN */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0066ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* ✅ What I fixed */&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#0066ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0066ff&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;&lt;strong&gt;The rule:&lt;/strong&gt; Never remove the default focus outline without replacing it. &lt;code&gt;:focus-visible&lt;/code&gt; is your friend—it shows the outline when using keyboard, but not when clicking with a mouse. Best of both worlds.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Unlabelled Form Inputs (The Invisible Ask)
&lt;/h3&gt;

&lt;p&gt;I have a contact form. The form had inputs, but they were only labeled visually—the HTML had no &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements.&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="c"&gt;&amp;lt;!-- ❌ What I had --&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;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ✅ What I should have --&amp;gt;&lt;/span&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;"contact-email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email address&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;"contact-email"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; A screen reader user reaches the input field and hears... nothing. They don't know what the field is for. The &lt;code&gt;placeholder&lt;/code&gt; disappears when they type. Without a label, they're flying blind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Even better:&lt;/strong&gt; If you want to visually hide the label (for design reasons), use CSS:&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;.sr-only&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&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="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rect&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="m"&gt;0&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-width&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"contact-email"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sr-only"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email address&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;"contact-email"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The label is there for assistive tech. The visual design is unchanged.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Keyboard Navigation Trap in My Project Modal
&lt;/h3&gt;

&lt;p&gt;I have a modal that displays project details. You Tab through the form, but when you reach the last button, Tab wraps back to the first button &lt;em&gt;inside the modal&lt;/em&gt;—you can't escape without clicking outside or pressing Escape.&lt;/p&gt;

&lt;p&gt;This is called &lt;strong&gt;focus trap without escape&lt;/strong&gt;, and it's particularly bad if you're keyboard-only.&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="c1"&gt;// ✅ Simple fix: Handle Tab in the last focusable element&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastButton&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;.modal .btn-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;lastButton&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;keydown&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;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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&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="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="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;.modal .btn-primary&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// OR, better: Use a proper focus trap library behavior&lt;/span&gt;
&lt;span class="c1"&gt;// But here's the core idea—don't trap users in a modal without a clear exit.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real talk:&lt;/strong&gt; The best fix is making sure your modal can be closed with Escape (which browsers often handle automatically), and that focus moves &lt;em&gt;back&lt;/em&gt; to the trigger element when the modal closes.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. No Skip Navigation Link
&lt;/h3&gt;

&lt;p&gt;This is the one that made me laugh at myself. I preach about skip links, and mine had zero.&lt;/p&gt;

&lt;p&gt;A skip link is a link at the very start of your HTML that says "Skip to main content." It's hidden by default, but visible when you Tab to it. For keyboard-only users, this saves them from Tabbing through your entire navigation menu every single page.&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;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#main-content"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"skip-link"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Skip to main content&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- navigation stuff --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"main-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- your actual content --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.skip-link&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&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="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&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="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.skip-link&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;top&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you Tab onto it, it slides into view. Click it, and you jump straight to &lt;code&gt;#main-content&lt;/code&gt;. Keyboard users say a silent thank you.&lt;/p&gt;




&lt;h2&gt;
  
  
  WCAG 2.2: What's New and What I Hit
&lt;/h2&gt;

&lt;p&gt;WCAG 2.2 added 9 new criteria in October 2023. I triggered at least two:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Focus Appearance (2.4.13)&lt;/strong&gt; — That focus indicator I removed? That's criterion 2.4.13. WCAG now demands a visible focus indicator with at least 3:1 contrast against adjacent colors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target Size (2.5.8)&lt;/strong&gt; — Interactive elements should be at least 24px × 24px (or 24px of spacing between them). My social media icons in the footer were 16px. Easy miss. Easy fix.&lt;/p&gt;

&lt;p&gt;Both are WCAG AA level. Both are things I didn't know about when I started, but both are &lt;em&gt;important&lt;/em&gt; because they make the web usable for more people.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Quick-Start Checklist: 10 Minutes
&lt;/h2&gt;

&lt;p&gt;If you only read this far, here's what to do right now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install axe DevTools&lt;/strong&gt;, open your site, run the scan. Note the failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check all images.&lt;/strong&gt; Does each &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; have useful alt text? (Not &lt;code&gt;alt="image"&lt;/code&gt;, actual description.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with Tab only.&lt;/strong&gt; Close your trackpad. Can you navigate your entire site with Tab and Enter? Is focus always visible?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check form labels.&lt;/strong&gt; Every input should have a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; with a matching &lt;code&gt;for&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test color contrast.&lt;/strong&gt; Pick your key text colors. Run them through WebAIM's contrast checker. Aim for 4.5:1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. Thirty minutes, max.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Thing About Being a Developer Who Cares
&lt;/h2&gt;

&lt;p&gt;I think we fail accessibility not because we're careless, but because the defaults are broken. The web platform doesn't make inclusive design automatic. You have to opt in.&lt;/p&gt;

&lt;p&gt;But once you see it—once you understand that real humans depend on this stuff—it becomes harder to ignore. And the good news is that it's not hard. It's just... intentional.&lt;/p&gt;

&lt;p&gt;I fixed my six issues in an afternoon. My portfolio loads faster, is more usable for everyone, and I can talk to clients with actual credibility now.&lt;/p&gt;

&lt;p&gt;You can too.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Priya Nair&lt;/strong&gt; is a frontend developer based in Amsterdam. She writes about accessibility, inclusive design, and the occasional CSS rabbit hole. When she's not auditing her own code, you'll find her advocating for the web that works for everyone.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
