Lighthouse audit button-name · Accessibility

Buttons do not have an accessible name: how to fix it

Quick answer: This audit fails when a button has no text a screen reader can announce, usually an icon-only close, menu, search or play button. Fix it by adding an aria-label to the button or visually hidden text inside it, and mark the icon aria-hidden. Screen readers otherwise announce it as just "button".
View raw .md for LLMs / your notes

This Lighthouse audit fails when a <button> on the page has no text a screen reader can announce. The usual cause is an icon-only button: a close ×, a hamburger menu, a search magnifier, a carousel arrow, a video play button. Sighted users see the icon. A screen reader reaches the button and says "button", which tells the user nothing about what it does.

TL;DR

What does the button-name audit check?

Lighthouse runs the axe-core rule button-name against the rendered DOM. Deque's own documentation titles the same rule "Buttons must have discernible text", so you will see both wordings in search results and in axe DevTools. They are the same check.

It computes the accessible name of each native <button> element, and fails if that name is empty. The name comes from the first of these that produces text:

  1. aria-labelledby pointing at another element
  2. aria-label on the button
  3. The text content of the button, including alt text of any image inside it
  4. title on the button

If all four are empty, the audit fails. The passing title reads "Buttons have an accessible name"; the failing one is "Buttons do not have an accessible name".

Two sibling audits cover the other button shapes, so a button can fail under a different id:

What does "Element does not have inner text that is visible to screen readers" mean?

When you expand a failing button in the Lighthouse report, each offender carries this block of text from axe-core:

Fix any of the following:
  Element does not have inner text that is visible to screen readers
  aria-label attribute does not exist or is empty
  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
  Element has no title attribute

Each line is one way the button could have got a name, and each one was checked and came up empty. "Fix any of the following" is literal: satisfying one line passes the audit. The first line means the button has no text content at all, or its only content is an icon, an SVG, or text hidden with display: none. The same block appears under the link-name audit for anchors, which is why the phrase is so widely searched on its own.

Why do buttons without an accessible name matter?

Buttons are where the page does things: open the menu, add to cart, close the dialog, submit the form. A nameless button is an action the user cannot identify.

How do I fix "buttons do not have an accessible name"?

Pick whichever of these fits the markup. All four produce a valid accessible name.

<!-- FAILS: icon-only button, nothing to announce -->
<button class="menu-toggle"><svg>...</svg></button>

<!-- FIX 1: aria-label on the button. Best for icon-only buttons. -->
<button class="menu-toggle" aria-label="Open menu">
  <svg aria-hidden="true" focusable="false">...</svg>
</button>

<!-- FIX 2: visually hidden text. Works without ARIA and survives page translation. -->
<button class="menu-toggle">
  <svg aria-hidden="true" focusable="false">...</svg>
  <span class="sr-only">Open menu</span>
</button>

<!-- FIX 3: alt text, when the button wraps an <img> -->
<button><img src="/icons/search.svg" alt="Search"></button>

<!-- FIX 4: real visible text, always the best option when you have room -->
<button>Add to cart</button>

The sr-only class, if your CSS framework does not already ship one (Tailwind calls it sr-only, Bootstrap 5 calls it visually-hidden):

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

Put aria-hidden="true" on the icon in fixes 1 and 2. Without it, some screen readers announce the SVG's own title or an icon font's glyph alongside your label. Hide the graphic and let the label speak.

Name the action, not the icon. "Close dialog", not "X". "Next slide", not "Right arrow". For a toggle, keep the name stable and expose state with aria-expanded or aria-pressed rather than swapping the label between "Open menu" and "Close menu".

What are the most common causes on a real site?

In a scan of 269 production storefronts, button-name failed on 22.7% of them (61 sites). Re-running live on failing stores, the offenders were the same few shapes every time:

How do I fix this in React, Next.js, WordPress, or Shopify?

React / Next.js

Icon components usually render an <svg>, so the name goes on the <button>, not the icon:

// FAILS
<button onClick={close}><XIcon /></button>

// FIXED
<button onClick={close} aria-label="Close dialog">
  <XIcon aria-hidden="true" />
</button>

If you build a shared IconButton component, make label a required prop and set aria-label from it, so a nameless icon button cannot compile. Libraries such as Radix and MUI already require or warn about a label on their icon buttons.

For carousel dots, name each one by position: aria-label={Go to slide ${i + 1}}. Catch the rest in CI with eslint-plugin-jsx-a11y for static markup, and @axe-core/react or Playwright with @axe-core/playwright for rendered output, since icon components hide the problem from linting.

WordPress

Shopify

The usual culprits live in the header, cart drawer and product sections:

<!-- FAILS -->
<button type="button" class="drawer__close">{% render 'icon-close' %}</button>

<!-- FIXED -->
<button type="button" class="drawer__close" aria-label="{{ 'accessibility.close' | t }}">
  {% render 'icon-close' %}
</button>

Use a translation key rather than a hardcoded string so the label follows the storefront locale. Dawn-based themes label their close, quantity and slider buttons already; older and heavily customised themes, and buttons injected by apps, frequently do not. If the offender comes from an app, the fix belongs to the app developer, so report it to them.

What button-name pitfalls should I avoid?

How do I verify the fix?

  1. Re-run Lighthouse. The button-name audit should pass and move into "Passed audits".
  2. In Chrome DevTools, select the button and open the Accessibility pane. Computed Properties → Name shows exactly what a screen reader will announce. Empty means it still fails.
  3. Check at mobile width as well as desktop. Labels hidden at one breakpoint are the most common reason a fix does not stick.
  4. Test with a screen reader: VoiceOver on Mac (VO+U, then arrow to Form Controls), or NVDA on Windows (Insert+F7, then Buttons). If you cannot tell what a button does from the list alone, the name is not good enough.
  5. Add axe DevTools or an axe check in your end-to-end tests to catch regressions before they ship.

Audit your URL at https://lighthouse-md.com.

Audit your page now

Paste your URL, get scores plus a CLAUDE.md plan for Claude Code.

Run audit →
Add us as a preferred source on Google

One click, and Google shows more of lighthouse-md.com in your search results.