Section 4 · Visual Design and Content
Accessible Names and Descriptions
18 min3 min readCode playground
Lesson at a glance
Predict the accessible name and description a browser computes for any control, and give icon buttons and repeated buttons names that are unique and match their visible label.
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Ticketly’s event page has three buttons that all say “Book now”, a close button that shows “×”, a search button containing only an SVG magnifying glass, and a newsletter button whose name a developer set to “newsletter-cta” for analytics. In a screen reader’s list of buttons, the page reads: “Book now, Book now, Book now, times, button, newsletter-cta”. Every one of those is a naming problem, and you fix all of them by understanding one algorithm.
How the browser computes a name
The W3C’s Accessible Name and Description Computation spec defines exactly how a browser picks a name. Simplified, it tries these sources in order and takes the first one that produces text:
aria-labelledby: the text of the referenced elements, joined with spaces, in the order of the ids.aria-label: the attribute’s string.- Native labelling: a
<label>for form fields,altfor images,<legend>for a fieldset,<caption>for a table,<title>for an SVG. - Content, for roles that allow it: buttons, links, headings, tabs, table cells, menu items and options. Text inside, plus the
altof images inside, minus anything witharia-hidden="true". title(andplaceholderfor inputs) as a last resort.
Two consequences matter every day. First, ARIA naming overrides visible text: once you add aria-label, the button’s own content is ignored. Second, names don’t merge: if aria-labelledby exists, the element’s aria-label is skipped. The one twist: if aria-labelledby lists the element’s own id, that self-reference resolves to its aria-label when it has one, not to its content. You can check any result in the browser: Chrome’s Accessibility pane and Firefox’s Accessibility Inspector both show the computed name.
Icon-only buttons
A button with only an icon has no text content, so it has no name, unless you give it one. Three options, in order of preference:
<!-- 1. Visible text next to the icon: best for everyone -->
<button type="button" class="search">
<svg aria-hidden="true" viewBox="0 0 24 24">…</svg>
Search
</button>
<!-- 2. Visually hidden text: the design stays icon-only -->
<button type="button" class="close">
<span aria-hidden="true">×</span>
<span class="visually-hidden">Close banner</span>
</button>
<!-- 3. aria-label: fine, but invisible to translation tools that skip attributes -->
<button type="button" class="close" aria-label="Close banner">
<span aria-hidden="true">×</span>
</button>Notice the aria-hidden="true" on the “×”. Without it, option 2’s name would be “× Close banner”, read by some screen readers as “times Close banner”.
Repeated buttons and Label in Name
Three “Book now” buttons are fine visually, because each sits in its card. In a list of buttons they are indistinguishable. You want names like “Book now, Friday pass” without changing the visible design. aria-labelledby can reference the button itself plus the card heading:
<article>
<h3 id="fri-title">Friday pass</h3>
<button type="button" id="fri-book" aria-labelledby="fri-book fri-title">Book now</button>
</article>The name becomes “Book now Friday pass”. The visible words come first, which matters for 2.5.3 Label in Name (A): the accessible name must contain the visible label, and best practice is to start with it. Dina, who uses voice control, says “click Book now”; her software finds the three matches and numbers them for her to choose. If the name had been “Reserve Friday” she would have no way to click it by saying what she sees. That is exactly what was wrong with the “newsletter-cta” button: the visible word “Subscribe” isn’t in the name at all. Delete the aria-label and the content provides the right name again.
Descriptions
A description is secondary information, announced after the name and role, usually after a short pause. aria-describedby works like aria-labelledby but feeds the description instead:
<label for="promo">Promo code</label>
<input id="promo" aria-describedby="promo-hint">
<p id="promo-hint">Codes are 8 letters, for example HARBOUR8.</p>NVDA reads “Promo code, edit text, Codes are 8 letters, for example HARBOUR8.” Use descriptions for hints, formats and error messages, as you did in the forms lesson. Keep the name short and the description optional: users can skip it, but they can’t skip a name.
Every control on Ticketly now has a name a person could say out loud. The last lesson of this section looks at the words themselves: plain language, and the language attributes that make Ticketly’s Arabic content sound right.
Key takeaways
- Browsers compute a control’s name in a fixed order: aria-labelledby, then aria-label, then native labelling (label, alt, legend, caption), then the element’s content, then title.
- aria-labelledby and aria-label override everything else, including visible text, so use them carefully.
- A visible label must appear in the accessible name (2.5.3 Label in Name), ideally at the start, so voice control users can say what they see.
- aria-describedby adds secondary information, read after the name and role; it is for hints and errors, not for the name itself.