"Buttons must have discernible text": how to name icon-only buttons and links

Updated Oct 27, 2026 · Pewterkit Guides

"Buttons must have discernible text" means a button has no accessible name, usually an icon-only button (a hamburger menu, an X to close, a heart, a social icon), so a screen reader announces just "button". Give it a name: visible text, an aria-label, or aria-labelledby pointing at visible text. The same goes for links whose only content is an icon or an image without alt text.

Why it matters

The axe rule button-name (impact Critical) says screen reader users can't tell what a button does without an accessible name; link-name (impact Serious) says "Link text and alternate text for images, when used as links, must be discernible by a screen reader." Both map to WCAG 4.1.2 Name, Role, Value; links also map to 2.4.4 Link Purpose (In Context).

Common cases in AI-built apps, and the fix

Icon button (Lucide, Heroicons, an SVG):

// Before: announced as "button"
<button onClick={openMenu}><Menu /></button>

// After
<button onClick={openMenu} aria-label="Open menu"><Menu aria-hidden="true" /></button>

Close button in a dialog:

<button type="button" aria-label="Close"><svg aria-hidden="true">…</svg></button>

Social or logo link with an image:

<a href="https://x.com/yourapp"><img src="/x.svg" alt="Acme Rota on X"></a>

Link with an icon only:

<a href="/cart" aria-label="Cart, 2 items"><svg aria-hidden="true">…</svg></a>

aria-hidden="true" on the icon stops screen readers reading the SVG's internals, and the label on the button or link does the naming.

Axe's failing patterns include an empty aria-label, and an aria-labelledby that points at an element that is missing or empty, so check the label actually contains words.

Prompt for your app builder

Find every button and link whose only content is an icon, SVG or image.
Give each an accessible name: aria-label on the button or link (for
example "Open menu", "Close", "Search", "Cart"), and aria-hidden="true"
on the decorative icon inside. For image links, set the image's alt to
where the link goes. List what you changed.

What Pewterkit Check looks for

A free check reads your homepage's server HTML and lists buttons and links that have no name it can find in the HTML: no text inside, no label attribute and no image alt. When the server HTML is an empty app shell, it makes no claim, because those controls only appear after JavaScript runs.

FAQ

What does "discernible text" mean?

Text a screen reader can announce as the control's name: words inside the button, an aria-label, aria-labelledby pointing at visible text, or the alt of an image inside a link.

Is a title attribute enough?

Axe lists title as a passing pattern, so it clears the rule. Visible text or an aria-label names the control just as well and keeps every control consistent.

My button has an icon from a component library. Where does the label go?

On the <button> element (or the component's prop that passes through to it), not on the icon. Mark the icon aria-hidden="true".

Do these labels change how my app looks?

They don't: aria-label is read by assistive technology and isn't displayed.

Sources

  1. W3C WAI: Understanding Success Criterion 4.1.2 Name, Role, Value — read 27 Oct 2026: every user interface component, including links and form elements, needs a name that can be programmatically determined
  2. axe rule button-name: Buttons must have discernible text (Deque University) — read 27 Oct 2026: fixes, failing patterns, WCAG 4.1.2
  3. axe rule link-name: Links must have discernible text (Deque University) — read 27 Oct 2026: link names, image links, WCAG 2.4.4 and 4.1.2

How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked Oct 27, 2026. Spotted something out of date? Tell us.

Related guides