( Robust / WCAG 4.1.2 )

Button or link has no accessible name

CriticalLevel AWCAG 4.1.2 — Name, Role, Value

What is this issue?

A <button> element, or an <a> element with an href, exposes no accessible name to assistive technology: no text node inside it, no aria-label attribute, and no aria-labelledby pointing at text elsewhere on the page. The control still has a role (button or link) and, for a link, a destination, but nothing identifies what it does or where it goes.

This almost always happens with icon-only controls: a hamburger menu button that’s just an SVG, a close “X,” a social-media icon link. The icon communicates its purpose visually, but an icon with no text alternative communicates nothing to a screen reader, which reads the DOM (the tree structure the browser builds from your HTML), not the rendered pixels.

Why does this matter?

When a screen reader lands on an empty button, it announces the role alone, “button,” with no indication of what pressing it does. A user hearing “button” with nothing else has to guess, explore the surrounding markup, or skip the control entirely rather than risk triggering an unknown action.

This is worse than a poorly-worded name. A button that says “Submit” is at least identifiable; a button that says nothing gives the user zero information to work with. On a page with several unlabeled icon buttons in a row (close, minimize, expand, delete), a screen reader user hears “button, button, button, button” and has no way to tell them apart without activating each one and observing what happens, which is a real risk when one of those buttons deletes something.

Who is affected?

  • Screen reader users: hear only “button” or “link” with no further information, and have no way to distinguish one unlabeled control from another without triggering it.
  • Voice control users: target controls by speaking their visible or accessible name, such as “click Close.” With no name to match, an empty button or link can’t be activated by voice at all, regardless of how visually obvious its icon is.

What users experience

Marcus uses JAWS on Windows to manage his team’s project dashboard. He tabs into a card showing a task, and JAWS announces “button” three times in a row for the row’s action icons (edit, duplicate, delete) with no way to tell which is which. He opens the card’s context menu instead, which happens to have text labels, just to find the delete action safely rather than risk pressing the wrong unlabeled button.

How do I fix it?

Add an accessible name to the button or link with visible text, aria-label, or a visually hidden span. This works because any of the three gives assistive technology a name to read: the difference between them is only whether sighted users also see that text on screen.

Prefer visible text whenever the design allows it, since it helps everyone, including users who don’t use assistive technology but still don’t recognize an icon’s meaning. When the design genuinely requires an icon-only control (a compact toolbar, a small close button in a corner), use aria-label to supply the name without changing the visual design, and add aria-hidden="true" to the decorative icon inside so a screen reader doesn’t try to read the SVG’s internal markup.

A visually hidden span (positioned off-screen with CSS rather than display: none) is a third option, useful when you want the exact same text to be both the accessible name and something you can style or reuse independently of an aria-label string buried in markup.

Code Examples

Before
<button>
  <svg><!-- hamburger icon --></svg>
</button>

<a href="/twitter">
  <svg><!-- twitter bird icon --></svg>
</a>
After
<!-- Method 1: aria-label (icon stays the only visible content) -->
<button aria-label="Open navigation menu">
  <svg aria-hidden="true"><!-- hamburger icon --></svg>
</button>

<!-- Method 2: visually hidden text (same name, styleable independently) -->
<button>
  <svg aria-hidden="true"><!-- hamburger icon --></svg>
  <span class="sr-only">Open navigation menu</span>
</button>

<a href="/twitter" aria-label="Follow us on Twitter">
  <svg aria-hidden="true"><!-- twitter bird icon --></svg>
</a>

Both button methods give the control a name of “Open navigation menu” instead of nothing. aria-hidden="true" on the SVG stops a screen reader from trying to read the icon’s internal <path> markup or a stray <title> element as if it were content, so the button’s own name is the only thing announced.

Framework Examples

Component libraries are the most common source of this rule’s failures: a shared IconButton component that forwards an onClick handler but has no required prop for an accessible name lets every consumer render an unlabeled button by default. Make the label prop required at the type level so a missing name is a build-time error, not a runtime accessibility bug.

type IconButtonProps = {
  icon: React.ReactNode;
  label: string; // required - no default, no optional (?)
  onClick: () => void;
};

function IconButton({ icon, label, onClick }: IconButtonProps) {
  return (
    <button aria-label={label} onClick={onClick}>
      <span aria-hidden="true">{icon}</span>
    </button>
  );
}

// Usage: <IconButton icon={<CloseIcon />} label="Close dialog" onClick={closeDialog} />

Making label a required, non-optional prop means TypeScript itself catches the mistake at compile time if a consumer forgets it; far more reliable than hoping every future usage of the component remembers to pass aria-label manually.

Common Mistakes

Mistake: “The icon is universally recognized, like a hamburger menu, so it doesn’t need a label.” Visual recognizability has no effect on what a screen reader announces. A hamburger icon and an empty <button> produce the exact same accessible name (none), regardless of how familiar the icon is to sighted users.

Mistake: “I added a tooltip with title, so the button is labeled.” A title attribute is a fallback at best, not a reliable accessible name. It only appears on mouse hover, several screen readers skip it, and it’s invisible to keyboard-only and touch users entirely. Treat it as a supplementary hover hint, never the sole naming mechanism.

Mistake: “The button has a visible icon and a colored background, so users can see it’s a button.” This describes what a sighted user perceives, not what’s exposed to the accessibility tree. Color and shape carry zero information to a screen reader, which only has the DOM’s role, name, and state to work with. The visual design and the accessible name are two entirely separate things that must both be built.

Mistake: “aria-hidden on the whole button hides the icon problem.” Adding aria-hidden="true" to the button itself removes the entire control from the accessibility tree: screen reader users can no longer find it at all, which is worse than an unlabeled button they could at least discover. aria-hidden belongs on the decorative icon inside, never on the interactive element itself.

How RedFlag Detects This

Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s button-name rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule inspects every <button> and <a href> element and computes its accessible name following the standard accessible-name algorithm: text content, then aria-label, then aria-labelledby, then a title fallback. Any control that resolves to an empty string after that computation is flagged.

False negative: axe-core confirms a name exists; it cannot judge whether that name is accurate for what the button actually does: a button labeled “Submit” that actually deletes a record passes this check even though the name is misleading. False positive: none typical for this check, since resolving to an empty accessible name is a binary condition axe-core evaluates reliably from the DOM. Manual step: for every flagged control, confirm the fix you add actually describes the action: “button” with a name is only half the job if the name itself is vague.

Manual Testing

  1. Open the page in Chrome or Firefox with NVDA or JAWS running (Windows), or Safari with VoiceOver (Mac).
  2. Tab through every button and link on the page, paying special attention to icon-only controls (close buttons, menu toggles, social icons).
  3. Listen to what’s announced after the role: it should be a specific action or destination (“Open navigation menu, button”), not just the bare role (“button”) with nothing after it.
  4. If two or more controls in a row announce identically or announce nothing, that’s a fail: each one needs its own distinct accessible name.

4.1.2 Name, Role, Value: Every interface component must expose a name, role, and current value to assistive technology. An empty button or link has a role but no name, which is exactly the “name” half of this criterion failing outright. This is the primary criterion the rule maps to.

Input button has no accessible name covers the same underlying failure for <input type="submit">, <input type="button">, and <input type="reset">, where the missing name comes from an empty value attribute instead of empty text content.

Link must have discernible text is the link-specific version of this same check, phrased around anchor elements that are empty or contain only an unlabeled image.

Interactive element has no accessible name at all covers the broader, non-button/link version of this problem across custom interactive components that RedFlag’s AI-assisted review surfaces.

Link text is generic and gives no context is the milder cousin of this rule: a link that has a name, just not a useful one, rather than no name at all.

Custom control built with an ARIA command role has no accessible name extends this same naming requirement to custom widgets using role="button" or role="link" instead of native HTML elements.

References

Frequently asked questions

Does an icon alone ever count as an accessible name?

No. An SVG or icon font glyph has no text content a screen reader can read unless it carries its own text alternative, and browsers do not generate one automatically from image content. You still need aria-label, visible text, or aria-labelledby on the button or link itself.

Is aria-hidden enough to fix an empty button?

No, and it makes things worse. Adding aria-hidden to the button removes it from the accessibility tree entirely, so keyboard and screen reader users cannot reach it at all. Use aria-hidden only on the decorative icon inside the button, and put the accessible name on the button itself.

Does a title attribute count as an accessible name for a button?

Technically it can act as a fallback, but do not rely on it. Screen reader support for reading title as a name is inconsistent, it never appears for keyboard-only or touch users, and it disappears the instant a mouse is not hovering. Use aria-label or visible text instead.

Why does this rule apply to links as well as buttons?

Both share the same underlying requirement in WCAG 4.1.2: every interactive element needs a name assistive technology can announce. An empty anchor behaves identically to an empty button for a screen reader user; both announce only a bare role with nothing to act on.

Does axe-core catch a button whose only content is a background-image icon with no SVG or img element?

Yes. The accessible name computation does not care how the icon is rendered visually. CSS background-image, inline SVG, or an img element with no alt all produce the same result: no text content, so no accessible name, so the check flags it the same way.