The accessible name

Make sure every control has a name, that the name is the right one, and that you know where it came from.

04of 16
≈ 13Min read
6Parts

One string, and everything depends on it

Every control in the accessibility tree has an accessible name — the thing announced when focus lands on it, the thing listed when somebody asks for every button on the page, and the thing a voice-control user says to press it. A button with no name is announced as "button", which is unusable, and cannot be operated by voice at all.

The name is computed, not stored. The browser walks a defined order and takes the first thing it finds.

The order the browser resolves it in

Higher wins. aria-label="Save" on a button whose text says "Save the order" means the visible text is thrown away and the button is announced as "Save" — which is a bug and a very common one.

#SourceExample
1aria-labelledbypoints at other elements; their text is joined
2aria-labela string you write
3the native label<label>, alt, <legend>, <caption>, a button’s own text
4titlea tooltip — a last resort, and invisible to touch
5nothingthe control is nameless
Never override visible text with something different

A voice-control user says what they see: "click Save the order". If the accessible name is "Save", nothing happens. WCAG has a criterion for exactly this — the name must contain the visible text.

The icon button

The second version is better than it looks: the text is real, so it survives a translation pass, and a voice-control user can say it. aria-hidden="true" on the icon stops the glyph being read as a word.

the most common nameless control on the web
<button>✕</button>                        <!-- announced as "times" or "button" -->

<button aria-label="Close the quote">✕</button>      <!-- good -->

<button>
  <svg aria-hidden="true" focusable="false">…</svg>  <!-- hide the decoration -->
  <span class="visually-hidden">Close the quote</span>
</button>                                            <!-- better: real text, hidden visually -->

Inputs: a label, not a placeholder

A placeholder does produce a name — and it disappears the moment somebody types, takes the person's memory as a dependency, is usually too low-contrast, and is not shown at all in some assistive settings. Use a real label and, if the design demands it, hide it visually rather than removing it.

three ways, two of them fine
<label for="email">Email address</label>
<input id="email" type="email" autocomplete="email">          <!-- best -->

<label>Email address <input type="email"></label>             <!-- also fine -->

<input type="email" placeholder="Email address">              <!-- a name, and a bug -->

Hiding text from eyes, not from everyone

display: none and visibility: hidden remove the element from the accessibility tree as well as from the screen; aria-hidden="true" removes it from the tree only. This utility removes it from the screen only — which is what "for screen readers" means.

the one utility every project needs
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

Names that say nothing

  • "Click here", "Read more", "Learn more" — a list of twenty links, all identical. The name should make sense read on its own: "Read the Vesper specification".
  • The same name twice on the same page for two different things.
  • A file name as alt text: alt="IMG_4821.jpg".
  • "Button" in the name: it is already announced as a button. "Submit button" becomes "submit button button".
  • A name that is only an emoji or a symbol, announced as its Unicode description.

Practice