Web Platform · HTML and Semantics

Accessible Name Computation: Which Attribute Wins

aria-labelledby, aria-label, label elements, alt text, and element content are consulted in a fixed order. The order, the recursion rules, and the roles that refuse a name entirely.

A button containing the text “Save” and an aria-label of “Save draft” is announced as “Save draft”. The visible text is not merged with the attribute, not appended, and not preferred; the attribute replaces it. A button with aria-labelledby pointing at that same element is announced with the text of the referenced element instead, even when the button’s own text differs, and even when the referenced element is hidden.

Accessible names are computed by a fixed algorithm with a strict precedence chain. Each source in the chain either produces a name or produces nothing, and the first one that produces a non-empty result ends the computation. Nothing accumulates except within a single source that concatenates several pieces.

The chain in order

The algorithm is defined in Accessible Name and Description Computation, and the browsers implement it with a small number of documented deviations. For an HTML element the order is:

  1. If the element or an ancestor is hidden and the element is not being referenced by another element’s aria-labelledby, the name is empty.
  2. aria-labelledby, when present and when the element is not already part of a traversal started by another aria-labelledby.
  3. aria-label.
  4. Host-language labelling: the value of a submit button, the alt of an image, label for a form control, caption for a table, legend for a fieldset, figcaption for a figure, summary for details, title inside an SVG.
  5. Name from content, for roles that permit it.
  6. The title attribute.

The host-language sources differ by element, which is why the specification writes this as a traversal of the element’s own semantics rather than as a flat list. Their position relative to aria-label does not vary: for an input type="submit" with both a value and an aria-label, the aria-label supplies the name, and the same is true of an img with both alt and aria-label. The attribute overrides the native labelling mechanism rather than deferring to it.

The consequence for authors is simple to state and easy to violate. Adding aria-label to an element that already has a <label> changes the announced name; it does not add to it. Adding aria-labelledby changes it again, and also removes the label from consideration entirely.

aria-labelledby is a reference, not a string

aria-labelledby takes a space-separated list of IDs. For each ID, the algorithm computes the text alternative of the referenced element — a full recursive computation, not just its textContent — trims the result, and joins the results with a single space. Missing IDs are skipped silently; there is no error and no fallback to aria-label for that entry.

Three properties follow from the recursion.

An element referenced by aria-labelledby is included even when it is hidden with display: none or visibility: hidden, and even when it is outside the accessibility tree for another reason. The exception exists so that a visible control can borrow a name from content that is visually redundant, which is common in tables and in dialogs with a heading that has already been read.

Self-reference is guarded. An element that lists its own ID is skipped at that point in the traversal rather than recursing forever. Mutual references between two elements are resolved by the same guard, and the result is that each element contributes its own content once.

The referenced element contributes its name, not its markup. If the reference points at a container holding an image with alt text plus a span with text, the computed name includes the alt text and the span text in document order, because name from content for that container walks its descendants. If the container holds a nested interactive element, the nested element contributes according to its own rules.

<h2 id="dialog-title">Delete repository</h2>
<button aria-labelledby="dialog-title repo-name">Delete</button>
<p id="repo-name">atlas</p>

The button’s name is “Delete repository atlas” — the heading text, then the paragraph text, joined with a space. The word “Delete” inside the button is never announced, because the labelledby branch returned a non-empty name before the content branch was reached.

The label element and its edge cases

A label associates implicitly by wrapping, or explicitly through for matching a control’s id. The accname computation for the control takes the label’s text alternative, which means descendant text and alt text of images inside the label, but not the value of the control itself. A label that wraps both a checkbox and its text therefore names the checkbox with the text only.

When a control has more than one associated label, the names are concatenated in tree order with spaces. This is legal and rare; it also means a stray second label anywhere in the document changes the name.

Two HTML-specific behaviours sit inside the label branch. A control nested inside a label is not part of the label’s own text alternative, which prevents the control’s value from appearing in its own name. And a label whose for points to a non-existent or non-labelable element associates with nothing, producing no name and no click behaviour.

alt="" is different from a missing alt. An empty alt declares the image decorative, and the image is exposed as presentational with no role and no name. A missing alt leaves an img with an image role and no name, which assistive technology may announce as an unlabelled image or as the file name depending on the platform. The two are not interchangeable, and checker tools treat them differently.

Roles that permit names from content

Name from content is not universal. It applies to roles whose accessible name is expected to be their contents: button, link, heading, cell, columnheader, rowheader, gridcell, option, tab, menuitem and its variants, checkbox, radio, switch, treeitem, tooltip, and row. For these, the algorithm walks the element’s children, including text nodes and CSS generated content from ::before and ::after, and concatenates what it finds.

For a textbox, content is the value, not the name, so a div with role="textbox" and text inside does not take its name from that text. The same applies to combobox, listbox, img, group, region, and the landmark roles: they take names only from explicit sources. A div with role="region" and no aria-label or aria-labelledby is an unnamed region, and unnamed regions are not exposed as landmarks.

A separate rule blocks naming entirely. Roles marked as name-prohibited — generic, presentation, none, and a few text-level roles — cannot carry an accessible name, and aria-label on them is ignored rather than exposed. This is why <div aria-label="Navigation"> does nothing: div maps to generic, which prohibits naming. The fix is to give the element a role that permits a name, such as nav or region, and then label it.

What placeholder and title are worth

placeholder is a hint about format, and HTML Accessibility API Mappings allows it as a name fallback for some text input types when nothing else supplies one. It is a weak source: it disappears as soon as text is typed, it is often a formatting example rather than a label, and several engines expose it as the description instead when a name exists. A placeholder without a label leaves a control whose name vanishes on input.

title is the last resort in the chain. When another source has already produced a name, the title is exposed as the accessible description rather than the name, which is the behaviour that makes tooltip text read as supplementary information. When it is the only source, the name is the tooltip text, and it is unavailable to touch users and unreliable for keyboard users.

Both are fallbacks that exist for content that cannot be fixed. A control labelled only by its placeholder passes some machine checks and fails the human one.

Practical resolution order when debugging

When the announced name is wrong, the fastest path is to enumerate the sources in the same order the algorithm uses and stop at the first non-empty one. Is there an aria-labelledby anywhere on the element or inherited from a wrapper? Does the element have an aria-label from a component library default? Is there a label that the component also renders, producing a concatenation? Does the role permit name from content, and if so, does the element contain text from an icon font or a hidden span?

A control whose label is rendered inside a shadow root needs the label inside the same root, or an explicit association, because aria-labelledby and label do not reach across the boundary. The accessibility tree inspector in browser developer tools shows the computed name and the computed description separately, which distinguishes the two title roles immediately. Comparing that output against the accessibility tree a screen reader builds is the only check that covers platform-specific remapping, and it is worth doing once per component rather than once per page.

The naming rules connect directly to the elements that carry structure. A named table uses its caption, a named fieldset uses its legend, and the semantics of both are covered in The Table Model, Foster Parenting, and Header Association.

References

  1. Accessible Name and Description Computation 1.2 — W3C
  2. HTML Accessibility API Mappings 1.0 — W3C
  3. WAI-ARIA 1.2 — W3C