DEV Community

Franklin
Franklin

Posted on

The Browser Already Has an Accessibility Architecture. Most Projects Ignore It.

Structural sibling to The Browser Already Has an Architecture. Most Projects Ignore It. — same distinction, different layer.

Also available in Español

The Problem

An accessibility audit comes back clean. Every interactive control has a role. Every icon has a label. The automated scanner shows a passing score, and the team moves on.

Then a screen reader user tries to close a navigation panel. They hear "Dismiss navigation panel" announced on an element that visibly says "Close menu." Nothing crashes. The control still works; pressing Enter still closes the panel. But the user who was told to listen for "Close" heard something else entirely, and had to guess.

Nobody removed a label. Someone added one, specifically to make the scanner stop flagging the control as unlabeled. The audit got quieter. The control got harder to use.

Why the Problem Exists

Accessibility tooling exists because accessibility failures are otherwise invisible to most of the people building a product. A missing label, a role nobody set, a focus order that traps a keyboard user: none of it shows up in a visual QA pass. Automated scanners and compliance checklists emerged to catch exactly that blind spot, and they catch a real category of failure: an icon-only button that announces as nothing but "button" to a screen reader.

For exactly that category, aria-label exists for a real reason. An icon-only control — a trash can glyph with no visible text — gives the browser nothing to compute a name from. There's no content for the accessible name computation to read. aria-label fills a gap that would otherwise sit empty.

The trouble starts when the same checklist that correctly flags an unlabeled icon button also flags a <button> that already has visible text, and the fix applied is identical either way: add an aria-label. One of those elements had nothing to work with. The other already had a name. The checklist doesn't distinguish between the two cases, because from the scanner's perspective, both just needed an aria-label to stop being flagged.

The First Principle

The browser doesn't wait for ARIA to tell it what something is. Native HTML elements already carry implicit roles and behavior — a <button> is a button to the accessibility tree before any attribute touches it, the same way it's already clickable and focusable before any JavaScript touches it. The accessibility tree is derived from the HTML itself, the same document the browser is already parsing for everything else.

Accessible names follow their own defined sequence, checked in order: aria-labelledby first, then aria-label, then the element's own content, then a few remaining fallbacks. A <button>Close menu</button> gets its name from the third step down that list, its content, because nothing earlier in the sequence is present to claim it first. The moment an aria-label is added, that earlier step wins instead, and the content a sighted user actually reads never gets reached.

ARIA isn't a separate accessibility layer sitting on top of the DOM. It's a set of inputs the same computation already runs, one that happens to take precedence over content when it's present, whether or not content was already saying the right thing.

Demonstrating the Principle

An icon-only control, with nothing else to name it:

<button aria-label="Delete item">
  <svg aria-hidden="true"><!-- trash can icon --></svg>
</button>
Enter fullscreen mode Exit fullscreen mode

Nothing here conflicts with anything. The <svg> is hidden from the accessibility tree, there's no text content for the computation to find, and aria-label supplies the only name available. This is the case ARIA was built for.

Now the same attribute, on a control that already had content:

<button aria-label="Dismiss navigation panel">
  Close menu
</button>
Enter fullscreen mode Exit fullscreen mode

Visually, nothing changed. A sighted user still reads "Close menu." Programmatically, the accessible name is now "Dismiss navigation panel." The content didn't disappear from the DOM. It simply stopped being the thing the name computation reaches, because aria-label sits earlier in the sequence and the computation stops at the first match.

Same attribute. Same syntax. One fills a gap. The other replaces something that was already correct.

The Pain Point

This shipped on a global navigation control: a native <button> with visible text reading "Close menu." An accessibility review flagged it during a compliance pass, not because it lacked a name, but because the checklist in use called for an explicit aria-label on every interactive control, regardless of whether one was already available from content. The label added was "Dismiss navigation panel," written to be more descriptive than the button's own short text.

The visible control and the programmatic name no longer described the same thing. A sighted user reads "Close menu." A screen reader announces "Dismiss navigation panel." Voice-control software listening for the word a user can actually see — the mechanism WCAG's Label in Name criterion exists to protect — has nothing to match. The user says "click close menu" to a control that, as far as the accessibility tree is concerned, isn't named that anymore.

The same review also added role="button" to a <div> elsewhere in the same navigation, standing in for a second control the team wanted to look the same. The role is a declaration, not a transfer. <div role="button"> tells the accessibility tree to announce the element as a button. It doesn't give that <div> a button's native keyboard handling, its default focus behavior, or its built-in activation on Enter and Space, all of which <button> already provides without being asked. The div announces correctly and does almost nothing correctly once a keyboard reaches it.

Saturday's Notes from the Pass strips the aria-label back off the navigation control and watches the accessible name return to "Close menu" on its own, then checks what the <div role="button"> is actually missing against what a native <button> would have supplied for free.

The Broader Lesson

This isn't a case against aria-label, or against ARIA generally. The icon-only button earlier in this piece needed it; there was nothing else to read. The failure here wasn't using the attribute. It was using it without checking whether the browser already had an answer.

Every annotation layered onto a document is a claim about what the platform doesn't already know. Sometimes that claim is correct — an icon with no text genuinely has nothing to name itself with. Sometimes it isn't, and the annotation overwrites something the browser was already computing correctly, counted as present on a checklist either way.

ARIA is a semantic intervention. It should supply what the native document doesn't already express, not replace what it does. The question worth asking before adding any of it isn't whether the control needs more accessibility information. It's whether the browser already has the information, and just hasn't been asked yet.

Top comments (0)