A while in the past, I shipped a part that felt accessible by each measure I might take a look at. Keyboard navigation labored. ARIA roles had been appropriately utilized. Automated audits handed with out a single grievance. And but, a display reader consumer couldn’t work out find out how to set off it. Once I examined it myself with keyboard-only navigation and NVDA, I noticed the identical factor: the interplay merely didn’t behave the way in which I anticipated.
Nothing on the guidelines flagged an error. Technically, every thing was “proper.” However in observe, the part wasn’t predictable. Right here’s a simplified model of the part that prompted the difficulty:
As you’ll be able to see within the demo, the markup is in no way sophisticated:
And the repair was a lot simpler than anticipated. I needed to delete the ARIA function attribute that I had added with the perfect intentions.
The markup is even simpler than earlier than:
That have modified how I take into consideration accessibility. The most important lesson was this: Semantic HTML does much more accessibility work than we often give it credit score for already — and ARIA is straightforward to abuse once we use it each as a shortcut and as a complement.
Many people already know the primary rule of ARIA: don’t use it. Effectively, use it. However not if the accessible advantages and performance you want are already baked in, which it was in my case earlier than including the function attribute.
Let me define precisely what occurred, step-by-step, as a result of I believe the my error is definitely a fairly frequent observe. There are a lot of articles on the market that say precisely what I’m saying right here, however I believe it usually helps to internalize it by listening to it by means of a real-life expertise.
Notice: This text was examined utilizing keyboard navigation and a display reader (NVDA) to watch actual interplay conduct throughout native and ARIA-modified parts.
1: Begin with the best doable markup
Once more, that is merely a minimal web page with a single native and no ARIA. And by default, it permits keyboard focus and demonstrates the performance of utilizing Tab, Enter, and House out of the field. Geoff just lately made this case when explaining the accessibility advantages of semantic HTML parts.
If the interplay triggers an motion, then that ingredient is a button. And on this case, the is designed to run a script that saves modifications to a consumer profile:
That single line provides us a shocking quantity without spending a dime:
- Keyboard activation with the
EnterandHousekeys - Right focus conduct
- A task that assistive expertise already understands
- Constant bulletins throughout display readers
At this level, there may be no ARIA — and that’s intentional. However I did have an present class for styling buttons in my CSS, so I added that:
2: Observe the native conduct earlier than including something
With simply the native ingredient in place, I examined three issues:
- Keyboard solely (Tab, Enter, House)
- A display reader (listening to how the management is introduced)
- Focus order throughout the web page
Every part behaved predictably. The browser was doing precisely what customers count on. This step issues as a result of it establishes a baseline. If one thing breaks later, you realize it wasn’t HTML that prompted it. Actually, we will see that every thing is in excellent working order by inspecting the ingredient in DevTool’s Accessibility panel.
3: Add properly‑intentioned ARIA
The issue crept in once I tried to make the button behave like a hyperlink:
I did this for styling and routing causes. This button wanted to be styled slightly otherwise than the default .cta class and I figured I might use the ARIA attribute slightly than utilizing a modifier class. You can begin to see how I let the styling dictate and affect the performance. A remains to be the right ingredient for this goal, however I needed it to appear like a hyperlink due to the design necessities. May as properly give that ingredient a hyperlink function then, proper?
On the floor, nothing appeared damaged. Automated instruments stayed quiet. However in actual use, the cracks confirmed rapidly:
- House now not activated the management reliably.
- Display screen readers introduced conflicting roles.
- Keyboard customers encountered conduct that didn’t totally match both a button or a hyperlink.
ARIA didn’t add readability right here; it launched ambiguity. However I had already “examined” my work and nothing was screaming at me that I’d conflated the ingredient’s function with one other sort of ingredient. Once more, all it takes is a fast take a look at DevTools.

4: Again to semantics
The repair wasn’t intelligent. It was subtractive. I reverted my types, used a category for styling, and went again to the semantic markup previous to altering the accessible function:
I do know it sounds straightforward: if it’s an motion, use a . If it takes you someplace, use a hyperlink (). However, in observe, we’re making choices with each key we sort and it’s simply as straightforward to conflate actions with locations. On this case, I completely used the right ingredient! My mistake was considering that ARIA was an applicable styling hook for my CSS.
As soon as the right ingredient was in place — absent of ARIA — the problems disappeared. As a substitute, I might outline a brand new classname and, you guessed it, use hold types with types.
Similar to that, I used to be in a position to type the ingredient how I wanted and the consumer who report the difficulty was in a position to verify that every thing labored as anticipated. It was an inadvertent mistake born of a primary misunderstanding about ARIA’s place within the stack.
Why this retains occurring
ARIA attributes are used to outline the character of one thing however they don’t redefine the behavioral default of the native parts. Once we override semantics, we quietly take accountability for:
- keyboard interactions,
- focus administration,
- anticipated bulletins, and
- platform‑particular quirks.
That’s a big floor space to keep up, and it’s why small ARIA modifications can have outsized and unpredictable results.
A rule I now comply with
Right here’s the workflow that has saved me essentially the most time and bugs:
- Use native HTML to precise intent.
- Check with keyboard and a display reader.
- Add ARIA solely to speak lacking state, to not redefine roles.
If ARIA feels prefer it’s doing heavy lifting, it’s often an indication the markup is preventing the browser.
The place ARIA does belong
One instance can be a easy disclosure widget utilizing a local plus aria-expanded to speak state — exhibiting ARIA used to add state, not substitute semantics.
This demo makes use of a local with aria-expanded, which is toggled with a sprinkle of JavaScript:
const button = doc.getElementById("toggle");
const panel = doc.getElementById("panel");
button.addEventListener("click on", () => {
const expanded = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", !expanded);
panel.hidden = expanded;
});
The accessible state (true/false) is communicated appropriately with out changing the button’s semantics.
Now, I do know that ARIA is important when:
- speaking expanded or collapsed state,
- saying dynamic updates,
- constructing really customized widgets, and
- exposing relationships HTML can’t categorical.
Used this fashion, ARIA enhances semantic HTML as an alternative of competing with it.
Let the platform be just right for you
The most important accessibility enchancment I’ve made wasn’t studying about extra attributes — it was trusting those browsers already perceive. Semantic HTML shouldn’t be the baseline you progress previous. It’s the muse that every thing else relies on.
And that’s what I actually hope you’re taking away from my expertise. All of us make errors. It’s a part of the job, sadly. However what good are they if we will’t study from them, even when it takes a tough lesson.
