skip to content

An audit flags <button aria-label="Add to cart">Buy now</button>. What problem does the mismatch between the aria-label and the visible text create, and how would you fix it?

level: seniorimportance: should knowfreq 36%

answer

  1. the name is the voice command
  2. aria-label replaces, never appends
  3. what you see must be what you say
  4. WCAG Label in Name
  5. visible words first, context after

basics

~20 s

The aria-label replaces the visible text in the accessibility tree, so a voice-control user who says "click Buy now" matches nothing and cannot activate the button. Make the accessible name start with, or contain, the visible words.

solid answer

~50 s

`aria-label` overrides the button's own content, so the accessible name is "Add to cart" while the screen says "Buy now". That breaks anyone whose input path goes through the name. Voice-control users speak what they can see — "click Buy now" — and the speech engine matches against the accessible name, so the command finds nothing. Screen-reader users who can also see the screen hear one phrase and read another, which is disorienting when following instructions or reading a page aloud with someone. WCAG's Label in Name criterion codifies this: the accessible name must contain the visible label text. The fix is to stop replacing and start extending — drop the `aria-label` if the visible text is already fine, or make it begin with the visible words and add the disambiguating detail after, as in `aria-label="Buy now, Nikon Z6 camera"`. Using `aria-labelledby` to reference the visible text plus an extra element does the same thing without an invisible duplicate string.

code

html · 9 lines
html
<!-- broken: spoken "click Buy now" matches nothing -->
<button type="button" aria-label="Add to cart">Buy now</button>

<!-- fixed: visible words lead, context follows -->
<button type="button" aria-label="Buy now, Nikon Z6 camera">Buy now</button>

<!-- better: no invisible duplicate string to drift -->
<h3 id="prod-42">Nikon Z6 camera</h3>
<button id="buy-42" type="button" aria-labelledby="buy-42 prod-42">Buy now</button>

go deeper

for a junior

Remember that aria-label replaces a button's visible text in the accessibility tree rather than adding to it, so a control that already reads clearly usually needs no aria-label at all.

for a middle

Explain who the mismatch harms and why: voice-control commands are matched against the accessible name, so a name that omits the visible words leaves the control unreachable by speech. Describe the containment fix.

for a senior

Bring the production angle — name the Label in Name criterion, show the aria-labelledby composition that cannot drift when copy changes, and explain how you would audit an existing codebase for the pattern.

for a principal

Own the prevention: component API design that makes the mismatch impossible, an automated check wired into CI, and a shared rule with design about when visible copy rather than an ARIA string is the thing to change.

## What actually breaks The accessible name is not a screen-reader-only field. It is the identifier that several input and output paths share: - **Voice control.** Software such as speech-recognition navigation matches an utterance like "click Buy now" against the accessible names of controls on the page. If the name is "Add to cart", the phrase the user can see matches nothing and the control is effectively unreachable by voice. - **Screen readers used with vision.** Many screen-magnifier and screen-reader users see the page. Hearing "Add to cart, button" while reading "Buy now" forces them to reconcile two labels for one control. - **Instruction-following.** Documentation, support agents and colleagues all say "press Buy now". Any user whose interface reports a different string is following a map that does not match the territory. - **Automated tooling and tests.** Selectors written against accessible names diverge from what the page displays, and audits flag the mismatch. This is exactly what WCAG 2.1's Success Criterion 2.5.3, *Label in Name*, requires: for controls with visible text, the accessible name must contain that text. Not "be similar to" — contain. ## Why the override happens at all ```html <button aria-label="Add to cart">Buy now</button> ``` A `<button>` is normally named by its own content. `aria-label` sits higher in the name computation, so its string replaces the content entirely rather than adding to it. Nothing warns you; the page looks unchanged, and only the accessibility tree tells the truth. The mismatch usually arrives with good intentions. Someone wanted more context for screen-reader users ("Add to cart" is clearer than "Buy now"), or the design copy changed from "Add to cart" to "Buy now" and the label was not updated, or a component takes an `ariaLabel` prop that a caller filled in with different wording than the children it renders. ## The fixes, in order of preference **Delete the attribute.** If "Buy now" is an adequate name, the button already names itself correctly. The best ARIA is often the ARIA you remove. **Start the name with the visible text.** ```html <button aria-label="Buy now, Nikon Z6 camera">Buy now</button> ``` The visible words are contained in the name — and, because voice-control matching typically works on prefixes and contained phrases, putting them at the *start* makes the spoken command most likely to work. The extra context then disambiguates a grid of otherwise identical buttons. **Compose from real elements instead.** ```html <h3 id="prod-42">Nikon Z6 camera</h3> <button id="buy-42" aria-labelledby="buy-42 prod-42">Buy now</button> ``` The name is built from the button's own visible text plus the visible product title, so it contains the visible label by construction and there is no invisible string to drift when the copy changes. This is the version that survives a copy edit six months later. **Change the visible text.** If "Add to cart" is genuinely the clearer wording, that is a design conversation, not an ARIA one — change what the button says and drop the attribute. ## The wider lesson Treat `aria-label` on an element that already has visible text as a defect by default. Its legitimate home is elements with *no* text to override — icon-only controls, landmarks that need distinguishing. The moment a control has visible words, those words are the contract with the user, and the accessible name has to honour it. A practical guard: in a shared component library, make the icon-only variant the one that accepts a label prop, and have the text variant derive its name from its children. That removes the opportunity for the two to diverge instead of relying on reviewers to spot it.

  • Does the visible text have to be at the start of the accessible name, or just somewhere in it?
    The conformance requirement is containment, so anywhere in the name satisfies it. Putting the visible words first is the practical choice: speech-recognition matching favours names that begin with the spoken phrase, so a leading match is the most reliable to activate. Trailing context after a comma reads naturally in a screen reader too.
  • How would you catch this class of bug before it reaches an audit?
    Automated accessibility checkers flag label-in-name mismatches, so run one in CI. Structurally, prevent it: in a component library, let text buttons derive their name from their children and reserve the label prop for icon-only variants, so a caller cannot supply wording that contradicts the visible copy. Reviewing any aria-label on an element that already has text is the manual backstop.
  • A designer wants the screen reader to say something clearer than the visible button text. What do you tell them?
    That the divergence is the problem, not the wording. If the clearer phrasing is better, change what the button displays; everyone benefits. If space forces terse visible copy, keep the visible words at the front of the accessible name and append the clarification, so the name contains what the user can see while still carrying the extra context.

saying these in an interview costs you the question

  • Believes aria-label is appended to the visible text
  • Treats the accessible name as screen-reader-only
  • Says voice control reads the on-screen text directly
  • Fixes it by hiding the visible text instead
  • Uses aria-label on controls that already have text

context