WCAG 4.1.2 · rule: button-name
How to fix buttons with no accessible name
A hamburger icon a screen reader reads as only "button" is a locked door. Here are four reliable ways to give every button an accessible name.
Last updated: · By Paweł Dziura
A hamburger that says nothing
Picture a screen-reader user landing on your homepage on their phone. The navigation is collapsed
behind the usual three-line "hamburger" icon. They swipe to it, and their screen reader announces:
"button." That's it. No hint of what it does, whether it opens a menu, submits something, or
blows up the site. So they don't press it — and your entire navigation is now a locked door. That
single icon button is the most common way a modern, JavaScript-heavy interface quietly excludes people,
and it's exactly what WCAG 2.1 success criterion 4.1.2 (Name, Role, Value, Level A)
exists to prevent. The axe rule that catches it is button-name.
Why buttons lose their names
A native <button> gets its accessible name from the text between its tags. The
trouble starts the moment that text is replaced by an icon. An SVG, an icon-font glyph, or a background
image carries no text for assistive technology to read, so the button is announced by its role alone.
The same happens with elements styled to look like buttons — a <div> or
<span> wired up with a click handler — which have neither a name nor a button role,
and can't be reached by keyboard at all.
<button><svg>...</svg></button>
<button class="icon-close"></button>
<div class="btn" onclick="submit()"><i class="fa fa-search"></i></div>
Four ways to give it a name
Any one of these resolves the failure. Pick the one that fits how the button is built.
-
Add an
aria-labelwhen the button is icon-only. This is the workhorse fix and reads naturally to a screen reader. -
Add visually-hidden text inside the button with a
sr-onlyutility, when you want the label to live in the DOM rather than an attribute. -
Point at existing text with
aria-labelledbywhen a nearby element already names the control. -
Use a real
<button>, never a clickablediv, so you get the role and keyboard behaviour for free.
<button aria-label="Open navigation menu">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
<button>
<span class="sr-only">Close dialog</span>
<svg aria-hidden="true">...</svg>
</button>
One detail that trips people up: mark the inner icon aria-hidden="true". Otherwise some
setups read both the icon's own title and your label, producing a stuttered "search search".
Don't forget the state
A toggle button — a menu opener, a "mute" control — needs more than a name; it needs to announce whether
it's on or off. Add aria-expanded="true|false" to a disclosure button, or
aria-pressed to a toggle, and update it in JavaScript when the state changes. A correctly
named button that never reports its state still leaves the user guessing.
How our scanner detects it
We run the button-name check against every element with a button role — native
<button>, <input type="button|submit">, and anything with
role="button" — and compute its accessible name the same way a browser does: inner text,
then aria-label, then aria-labelledby, then title. An empty
result is a hard failure we report with the element's selector and markup. What we can't judge
automatically is whether a present label is a good one — a button named "button" or "click"
passes the machine check and still helps nobody — so quality of wording is something we surface for
manual review rather than pass silently.