ESC

Type to search the knowledge base.

ARIA Labels and Descriptions

aria-label, aria-labelledby, and aria-describedby — when to use each, how they combine, and mistakes that silence or double-announce UI.

intermediate3 min read
  • accessibility
  • aria-labels

Accessible names and descriptions come from HTML first. ARIA attributes override or supplement when native association isn’t enough. Misuse hides visible text from AT or creates redundant noise.

Docs: ARIA label attributes, MDN aria-label.

The three attributes

Attribute Role
aria-label String name on the element
aria-labelledby Name from other elements’ text (by id)
aria-describedby Description / help from other elements

Name ≠ description. Name identifies; description adds detail.

Prefer native label

<label for="email">Email</label>
<input id="email" type="email" />

Don’t replace this with aria-label="Email" on the input while hiding the visible label without a good reason — sighted users need the label too.

When aria-label helps

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

Icon-only controls. Keep the label short and unique in context.

aria-labelledby

<h2 id="dialog-title">Delete project?</h2>
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
  …
</div>

Multiple ids concatenate:

aria-labelledby="product-name price"

aria-describedby for hints and errors

<label for="pw">Password</label>
<input id="pw" type="password" aria-describedby="pw-hint pw-err" />
<p id="pw-hint">Use at least 8 characters.</p>
<p id="pw-err" class="error">Password too short</p>

See accessible forms errors.

Conflict rules of thumb

  1. Visible label + aria-label with different text → AT may ignore visible text (label wins as name via aria-label). Keep them consistent or drop aria-label.
  2. Prefer labelledby to reuse visible headings.
  3. Don’t aria-label every landmark — use native <nav>, <main>.

Testing Library connection

screen.getByRole('button', { name: /close dialog/i });

Names come from the same computation — accessible names.

Footguns

  1. Empty aria-label="".
  2. IDs in labelledby that don’t exist.
  3. Descriptions that only exist visually via color.
  4. Translating visible text but forgetting aria-label strings.
  5. Using aria-label on non-focusable static text unnecessarily.

Interview out-loud answer

“I use native labels first. aria-label for icon-only controls, labelledby to point at visible titles, describedby for hints and errors. I verify the computed name in DevTools and keep aria strings in sync with i18n.”

Internationalization trap

<button aria-label="Close">×</button>

If visible UI is translated but aria-label stays English, SR users get a mixed-language product. Pipe all aria strings through the same i18n catalog as visible copy.

Naming groups of controls

<fieldset>
  <legend>Shipping speed</legend>
  <label><input type="radio" name="ship" /> Standard</label>
  <label><input type="radio" name="ship" /> Express</label>
</fieldset>

fieldset/legend names the group better than only aria-label on each radio. Each radio still needs its own per-option label.

When not to use aria-label

  • On static non-interactive text already visible
  • To override a bad visible label instead of fixing it
  • On wrapper <div>s that shouldn’t be exposed

Decision tree

Native <label for> first → visible control text second → icon-only aria-label / visually hidden text → extra help via aria-describedby, not an overlong name.

Tooltips vs descriptions

Tooltip text is often pointer-only and not consistently exposed as an accessible description. If the information is required to operate the control, put it in visible text or aria-describedby, not only a hover tooltip.

Further reading

Related guides