Klar Barrierefrei / wcag

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-label when 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-only utility, when you want the label to live in the DOM rather than an attribute.
  • Point at existing text with aria-labelledby when a nearby element already names the control.
  • Use a real <button>, never a clickable div, 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.