<?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: A11Y Practice</title>
    <description>The latest articles on DEV Community by A11Y Practice (@a11y_practice_be4b9244508).</description>
    <link>https://dev.to/a11y_practice_be4b9244508</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4140425%2F40a93fda-b6b9-4940-a9c9-64c391dfab46.png</url>
      <title>DEV Community: A11Y Practice</title>
      <link>https://dev.to/a11y_practice_be4b9244508</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/a11y_practice_be4b9244508"/>
    <language>en</language>
    <item>
      <title>A Skip Link That Doesn't Land Is Not a Bypass: WCAG 2.4.1 in Practice</title>
      <dc:creator>A11Y Practice</dc:creator>
      <pubDate>Wed, 14 Oct 2026 11:29:55 +0000</pubDate>
      <link>https://dev.to/a11y_practice_be4b9244508/a-skip-link-that-doesnt-land-is-not-a-bypass-wcag-241-in-practice-3oa</link>
      <guid>https://dev.to/a11y_practice_be4b9244508/a-skip-link-that-doesnt-land-is-not-a-bypass-wcag-241-in-practice-3oa</guid>
      <description>&lt;p&gt;You press Tab on a page and VoiceOver says &lt;strong&gt;link, Home, Primary, navigation&lt;/strong&gt;. Not the content. On a real site that header can hold dozens of links, and keyboard and screen reader users have to tab through it on every page load. Add a skip link and the problem looks solved. But if its &lt;code&gt;href&lt;/code&gt; points at an id that doesn't exist, activation goes nowhere.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/uuevKPHlgDk" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;WCAG &lt;strong&gt;2.4.1 Bypass Blocks&lt;/strong&gt; (Level A) requires a way to skip blocks of content that repeat across pages. A skip link only counts if it actually moves the user past the repeated block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways it breaks
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No skip link.&lt;/strong&gt; The first focusable node is &lt;code&gt;link "Home"&lt;/code&gt; inside &lt;code&gt;navigation "Primary"&lt;/code&gt;. You only reach &lt;code&gt;main&lt;/code&gt; after every nav link. There's no mechanism at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skip link with a mismatched target.&lt;/strong&gt; This one looks fixed. The link is first in Tab order and visible on focus, and VoiceOver announces it correctly: &lt;em&gt;visited, link, Skip to main content&lt;/em&gt;. But the code looks like this:
&lt;/li&gt;
&lt;/ol&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"&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;header&amp;gt;&lt;/span&gt;Study Lab&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Primary"&lt;/span&gt;&lt;span class="nt"&gt;&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;"skip-to-content-broken-case2-main"&lt;/span&gt;&lt;span class="nt"&gt;&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;p&gt;There's no element with &lt;code&gt;id="main"&lt;/code&gt;. After activation, focus is still inside the navigation. The problem isn't what the link says. It's where it goes, and you only see that in the accessibility tree, not in the announcement.&lt;/p&gt;

&lt;p&gt;Auditors test activation, not presence: press Enter and check where focus goes. Landmarks like &lt;code&gt;nav&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt; help screen reader users, but sighted keyboard users have no landmark shortcut, so a broken skip link is still a defect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Make the skip link the first focusable element, make its &lt;code&gt;href&lt;/code&gt; fragment match a real id, and give the target &lt;code&gt;tabindex="-1"&lt;/code&gt; so it can take programmatic focus:&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;class=&lt;/span&gt;&lt;span class="s"&gt;"skip-link"&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="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;header&amp;gt;&lt;/span&gt;…&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Primary"&lt;/span&gt;&lt;span class="nt"&gt;&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="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt;&lt;span class="nt"&gt;&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;p&gt;In React/JSX that's &lt;code&gt;tabIndex={-1}&lt;/code&gt;. &lt;code&gt;-1&lt;/code&gt; lets &lt;code&gt;main&lt;/code&gt; take focus from script or fragment navigation without adding it to the Tab order.&lt;/p&gt;

&lt;p&gt;Hide it until it's focused:&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;.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;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-9999px&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;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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;1rem&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;1000&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;h2&gt;
  
  
  Behaviors to verify
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The first Tab on the page reaches the skip link, and it's visible when focused.&lt;/li&gt;
&lt;li&gt;Pressing Enter moves focus into the main content, so the next thing read is content, not navigation.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;href&lt;/code&gt; fragment matches an id that exists exactly once, and the target has &lt;code&gt;tabindex="-1"&lt;/code&gt;. Check it in the accessibility tree and with a screen reader before shipping.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practice it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Video: &lt;a href="https://youtu.be/uuevKPHlgDk" rel="noopener noreferrer"&gt;https://youtu.be/uuevKPHlgDk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Short: &lt;a href="https://youtube.com/shorts/gnGrnoXHhCI" rel="noopener noreferrer"&gt;https://youtube.com/shorts/gnGrnoXHhCI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Companion write-up: &lt;a href="https://www.wasexamprep.com/blog/skip-link-is-not-a-bypass" rel="noopener noreferrer"&gt;https://www.wasexamprep.com/blog/skip-link-is-not-a-bypass&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Takeaway: a skip link that doesn't land is not a bypass. Test activation, not presence.&lt;/p&gt;

&lt;p&gt;If this was useful, subscribe to &lt;strong&gt;A11Y in Practice&lt;/strong&gt; on YouTube (&lt;a href="https://www.youtube.com/@a11yinpractice" rel="noopener noreferrer"&gt;@a11yinpractice&lt;/a&gt;) for more hands-on accessibility episodes, and visit &lt;a href="https://www.wasexamprep.com" rel="noopener noreferrer"&gt;wasexamprep.com&lt;/a&gt; for WAS study notes and labs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A11Y in Practice and wasexamprep.com are independent educational resources. They are not affiliated with or endorsed by IAAP (International Association of Accessibility Professionals).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>wcag</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>A Custom Div Is Not a Checkbox: WCAG 4.1.2 Name, Role, Value</title>
      <dc:creator>A11Y Practice</dc:creator>
      <pubDate>Wed, 07 Oct 2026 08:05:22 +0000</pubDate>
      <link>https://dev.to/a11y_practice_be4b9244508/a-custom-div-is-not-a-checkbox-wcag-412-name-role-value-m2c</link>
      <guid>https://dev.to/a11y_practice_be4b9244508/a-custom-div-is-not-a-checkbox-wcag-412-name-role-value-m2c</guid>
      <description>&lt;p&gt;You build a toggle that looks like a checkbox. You style two shipping options as buttons. Sighted users get it. Then you open VoiceOver.&lt;/p&gt;

&lt;p&gt;On the custom toggle, from a real capture: &lt;strong&gt;Email notifications (custom)&lt;/strong&gt; - and the hint: you are currently on a &lt;strong&gt;button, group&lt;/strong&gt;. No checkbox role. No checked state. Beside it, the native control announces as a &lt;strong&gt;checkbox&lt;/strong&gt;. Same job. Completely different exposure.&lt;/p&gt;

&lt;p&gt;WCAG &lt;strong&gt;4.1.2 Name, Role, Value&lt;/strong&gt; says the role and value must be programmatically determinable. Native checkbox, radio, and fieldset give you that for free. A click-only &lt;code&gt;div&lt;/code&gt; or plain &lt;code&gt;button&lt;/code&gt;s that fake selection fail 4.1.2 - missing roles, missing value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this trips people up
&lt;/h2&gt;

&lt;p&gt;Two failure patterns show up a lot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Custom "checkbox" with no widget role&lt;/strong&gt; - a clickable &lt;code&gt;div&lt;/code&gt; that looks finished. In the accessibility tree you may only get StaticText. Tab can skip it entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buttons faking a radio group&lt;/strong&gt; - sibling buttons with no &lt;code&gt;radiogroup&lt;/code&gt; parent and no "1 of 2" / "2 of 2". They look like a choice group. VoiceOver hears two independent buttons.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What to do instead
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prefer &lt;strong&gt;native&lt;/strong&gt; checkbox / radio / fieldset when you can&lt;/li&gt;
&lt;li&gt;When you must build custom: &lt;code&gt;role="checkbox"&lt;/code&gt; + focusability + &lt;code&gt;aria-checked&lt;/code&gt;, or &lt;code&gt;role="radiogroup"&lt;/code&gt; with &lt;code&gt;role="radio"&lt;/code&gt; on each option&lt;/li&gt;
&lt;li&gt;Extra ARIA is not the goal - the correct widget roles are. Then verify with a screen reader&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practice it
&lt;/h2&gt;

&lt;p&gt;I published a walkthrough on A11Y in Practice with broken vs fixed paths on the same free lab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video: &lt;a href="https://www.youtube.com/watch?v=PJh5PMdxHwA" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=PJh5PMdxHwA&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lab: &lt;a href="https://wasexamprep.com/labs/custom-controls/control-roles" rel="noopener noreferrer"&gt;https://wasexamprep.com/labs/custom-controls/control-roles&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Write-up: &lt;a href="https://www.wasexamprep.com/blog/custom-div-is-not-a-checkbox" rel="noopener noreferrer"&gt;https://www.wasexamprep.com/blog/custom-div-is-not-a-checkbox&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Short: &lt;a href="https://www.youtube.com/shorts/zz-ob1zySyY" rel="noopener noreferrer"&gt;https://www.youtube.com/shorts/zz-ob1zySyY&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Part of WAS Exam Prep / WAS Lab study notes (independent; not affiliated with IAAP or W3C).&lt;/p&gt;

&lt;p&gt;Takeaway: a custom &lt;code&gt;div&lt;/code&gt; that looks like a checkbox is not a checkbox until assistive tech gets the role and value - prefer native HTML when you can.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>wcag</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Static Text Is Not a Status Message: WCAG 4.1.3 Live Regions</title>
      <dc:creator>A11Y Practice</dc:creator>
      <pubDate>Wed, 30 Sep 2026 17:03:24 +0000</pubDate>
      <link>https://dev.to/a11y_practice_be4b9244508/static-text-is-not-a-status-message-wcag-413-live-regions-clk</link>
      <guid>https://dev.to/a11y_practice_be4b9244508/static-text-is-not-a-status-message-wcag-413-live-regions-clk</guid>
      <description>&lt;p&gt;You click &lt;strong&gt;Add item&lt;/strong&gt;. The cart count ticks from zero to one. You click &lt;strong&gt;Save&lt;/strong&gt;. The status flips to Saved. Sighted users see it instantly. On that button with VoiceOver, what do you hear?&lt;/p&gt;

&lt;p&gt;Often: still the button hint - and nothing about the cart or Saved. Static text on the page is not a status message.&lt;/p&gt;

&lt;p&gt;WCAG &lt;strong&gt;4.1.3 Status Messages&lt;/strong&gt; is about announcing that kind of update &lt;strong&gt;without moving focus&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this trips people up
&lt;/h2&gt;

&lt;p&gt;Two failure patterns show up a lot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No live region at all&lt;/strong&gt; - the DOM updates, the screen updates, assistive tech stays quiet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The trap&lt;/strong&gt; - &lt;code&gt;aria-live&lt;/code&gt; (or &lt;code&gt;role="status"&lt;/code&gt;) is on a wrapper, but the &lt;strong&gt;changing text sits outside&lt;/strong&gt; that node. The live region never sees a relevant update.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What to put on the changing node
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-live="polite"&lt;/code&gt; and/or &lt;code&gt;role="status"&lt;/code&gt; on the element whose &lt;strong&gt;text content&lt;/strong&gt; actually changes&lt;/li&gt;
&lt;li&gt;Prefer polite for non-urgent cart/status UI; keep assertive for rare urgent cases&lt;/li&gt;
&lt;li&gt;Do not assume "I added aria-live somewhere nearby" is enough - verify with a screen reader&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practice it
&lt;/h2&gt;

&lt;p&gt;I published a walkthrough on A11Y in Practice with broken vs fixed paths on the same free lab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video: &lt;a href="https://www.youtube.com/watch?v=td4DaFK8AYw" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=td4DaFK8AYw&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lab: &lt;a href="https://wasexamprep.com/labs/live-regions/polite-live-regions" rel="noopener noreferrer"&gt;https://wasexamprep.com/labs/live-regions/polite-live-regions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Short: &lt;a href="https://www.youtube.com/shorts/4sUPQ2snbHU" rel="noopener noreferrer"&gt;https://www.youtube.com/shorts/4sUPQ2snbHU&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Part of WAS Exam Prep / WAS Lab study notes (independent; not affiliated with IAAP or W3C).&lt;/p&gt;

&lt;p&gt;Takeaway: put the live region on the &lt;strong&gt;changing text node&lt;/strong&gt;, not on a silent wrapper.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>wcag</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Focusable Is Not Keyboard-Operable: WCAG 2.1.1 on Custom Controls</title>
      <dc:creator>A11Y Practice</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:20:33 +0000</pubDate>
      <link>https://dev.to/a11y_practice_be4b9244508/focusable-is-not-keyboard-operable-wcag-211-on-custom-controls-1hm4</link>
      <guid>https://dev.to/a11y_practice_be4b9244508/focusable-is-not-keyboard-operable-wcag-211-on-custom-controls-1hm4</guid>
      <description>&lt;p&gt;You Tab to a custom checkbox. The focus ring shows up. You press Space - nothing. Click with the mouse and it toggles.&lt;/p&gt;

&lt;p&gt;So is it accessible?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WCAG 2.1.1 Keyboard&lt;/strong&gt; says the &lt;em&gt;functionality&lt;/em&gt; must be available from a keyboard. Landing focus on the control is not enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why tools miss it
&lt;/h3&gt;

&lt;p&gt;In the broken lab case, the accessibility tree still exposes a checkbox with the right name and checked state. VoiceOver can announce it like a normal checkbox. The gap is the missing key handler (&lt;code&gt;onClick&lt;/code&gt; only).&lt;/p&gt;

&lt;p&gt;Same story for a custom radiogroup where every option has &lt;code&gt;tabIndex={0}&lt;/code&gt; and arrow keys do nothing: the tree looks mostly fine; keyboard testing does not.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to verify manually
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Checkbox / switch: Space toggles&lt;/li&gt;
&lt;li&gt;Radiogroup: arrows move selection; preferably one tab stop (roving tabindex)&lt;/li&gt;
&lt;li&gt;Do not trust "focusable + role" alone&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practice it
&lt;/h3&gt;

&lt;p&gt;I published a walkthrough on &lt;strong&gt;A11Y in Practice&lt;/strong&gt; with broken vs fixed paths on the same free lab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video: &lt;a href="https://youtu.be/7B1GsZny4pc" rel="noopener noreferrer"&gt;https://youtu.be/7B1GsZny4pc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lab: &lt;a href="https://wasexamprep.com/labs/custom-controls/keyboard-operation" rel="noopener noreferrer"&gt;https://wasexamprep.com/labs/custom-controls/keyboard-operation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Short: &lt;a href="https://youtube.com/shorts/Fii5VUN9GhI" rel="noopener noreferrer"&gt;https://youtube.com/shorts/Fii5VUN9GhI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog: &lt;a href="https://wasexamprep.com/blog/focusable-is-not-keyboard-operable" rel="noopener noreferrer"&gt;https://wasexamprep.com/blog/focusable-is-not-keyboard-operable&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Part of WAS Exam Prep / WAS Lab study notes (independent; not affiliated with IAAP or W3C).&lt;/p&gt;

&lt;p&gt;If this helped, the takeaway I keep repeating: &lt;strong&gt;roles name the widget. Keys make it usable.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>wcag</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
