Accessibility · Assistive Technology
Accessible Name Computation
How a screen reader derives the single name string for an element from text, labels, aria-labelledby, and fallbacks, and why precedence rules matter.
Every element a screen reader announces has exactly one string called its accessible name. That name is what the reader says for the element before it says anything else: the word “Save”, the label “Email address”, the heading text. The string is not stored anywhere in the page; it is computed, on demand, from the element’s own content and from attributes that point at other content. The algorithm that performs this computation is standardized so that a browser, a native app toolkit, and a screen reader all arrive at the same string for the same element. When they disagree, the user hears a name that is not the one the developer intended.
The precedence chain
The computation is a search through candidate sources in a fixed order, stopping at the first source that yields a non-empty string. That order, from highest to lowest priority, is roughly: an explicit aria-labelledby reference, an explicit aria-label attribute, an element-specific source of a label such as the <label> element associated with an input, the element’s own contents, a title attribute, and finally nothing at all. The details vary by role and platform, but the shape is consistent: explicit attributes win over implicit structure, and the author can always override the natural name.
The highest-priority source, aria-labelledby, is a list of element IDs. The computation joins the text of those referenced elements, in the order given, into a single string. Because the references are resolved at name-computation time, not at parse time, the referenced elements can be anywhere in the document and can themselves be hidden from view. This is the mechanism that lets a developer place a visible label in one part of the DOM and attach it to a control in a completely different part. The ID list is also the reason a name can be assembled from several fragments: one reference to the field’s label, another to a hint, a third to a unit suffix.
aria-label is lower priority but simpler: it is a literal string on the element itself. It wins over any implicit source, so a developer can rename a control without touching its content. The practical rule that falls out of the precedence is that aria-labelledby beats aria-label, which beats the native label mechanism, which beats the content.
The native label source
For form controls the natural source is the <label> element. A label is associated with a control in two ways: by nesting the control inside the label, or by matching the label’s for attribute to the control’s id. When a label is associated, its text is the control’s name. The association matters more than the adjacency: a label that merely appears next to an input in the source but is not associated contributes nothing, and the control ends up unnamed.
For controls that are not form fields, the natural source is the element’s own text content. A button named by its content is the most common case: the string “Save” written between the tags becomes the name. Here the computation is recursive. The name of an element whose name source is its content is the concatenation of the names of its children, which themselves are computed by the same algorithm. A link containing an image and a text run gets a name that is the image’s alt text followed by the text run. A button containing only an icon with no alt text gets an empty name, which is the classic case of a control that announces as nothing at all.
The recursion has a subtlety that trips up developers. When an element uses aria-labelledby or aria-label, the computation stops descending into its children for the purpose of its own name. The children can still be named independently if they are themselves focusable or exposed, but their text is not consulted for the parent’s name. This is why a button with an aria-label and descriptive child text announces only the aria-label: the override cuts the content source off.
Hidden content and whitespace
Two rules about which text counts are worth stating precisely because they cause most of the surprises.
Hidden content does not contribute to a name, with one important exception. Text inside a subtree that is visually hidden with display: none or visibility: hidden, or clipped to zero size, is normally not part of the accessible tree and so not part of any name. But content referenced by aria-labelledby is used even if it is hidden. This is deliberate: it lets authors write a label string they want screen readers to hear but sighted users not to see, such as a concise phrasing of a long visible sentence. The exception is the reason aria-labelledby points at hidden elements so often.
Whitespace is normalized but not removed in the way one might expect. Leading and trailing whitespace is trimmed, and interior runs of whitespace collapse to a single space, but the text is otherwise taken literally. A name assembled from three fragments gets a single space between each fragment, not a newline or nothing. This normalization happens at the string level before the string reaches the speech stage, so a screen reader never has to decide how to pronounce a line break inside a name.
What the screen reader does with the name
The name is the first thing spoken, but it is not the only thing. The reader concatenates the name with the role and the value and state, in an order that the reader chooses, and then speaks the whole phrase. A checked checkbox might announce as “Remember me, checkbox, checked”: name first, then role, then state. An input announces as “Email address, edit, has text”. The name is the only part the developer controls directly through content; the role and state come from the semantics of the element, and the order is the reader’s own.
This division of labour is the reason an empty name is so damaging. If the name is empty, the reader still says the role and state, so the user hears “checkbox, checked” with no idea what the checkbox is for. Every other stage of the announcement pipeline can be perfect, and the output is still useless, because the one string the developer is responsible for was never produced.
Embedded controls and the flattening rule
The content recursion has a stated boundary: when it descends into children, the name computation ignores the contribution that certain embedded controls would make through their own value. The specification’s step for concatenating descendant text excludes the values of embedded controls such as a nested text field or a nested button; their value does not become part of the enclosing name. A field labeled by a paragraph that contains a live text input does not have its name change on every keystroke, which is the point of the exclusion: a name that changed on every keystroke would make the control difficult to target and announce consistently.
The flattening rule also has an ordering subtlety for elements that carry both text children and a nested element with its own alt or label. The computation walks the subtree in document order and concatenates in that order, so the name of a link that wraps an image followed by a caption is the image’s alt text followed by the caption text, in the order they appear. Authors who expect the visible caption alone to be the name get a concatenation instead, and the remedy is an explicit aria-labelledby pointing at whichever single node holds the intended string.
A sibling algorithm, the accessible description computation, shares the machinery but fills a different slot in the announcement. A description is computed from aria-describedby, from a table caption, or from a title attribute when no other source applies, and the reader speaks it after the name and role rather than instead of them. The precedence chains differ in detail, most notably in which hidden content counts, but the structural lesson is the same: both strings are derived, both follow a fixed candidate order, and both are wrong for an element only when the author pointed the computation at the wrong source.
Where the algorithm gets hard
The full specification is considerably larger than the precedence chain above, because it must handle role-specific sources: the alt attribute for images, the value of an <option> inside a <select>, the caption of a table, the placeholder of an input when nothing better exists. It must also handle the case where a name is computed inside a shadow DOM boundary and where a referenced element lives in a different document. The existence of a single normative document for all of this is what keeps the browser and the screen reader from each inventing their own answer. The announcement pipeline resolves the name by running this exact algorithm, so the two mechanisms are not merely related; the name computation is the stage inside the pipeline that produces the string the user actually hears.
References
- Accessible Name and Description Computation 1.2 — W3C
- WAI-ARIA 1.2 — W3C
- HTML Living Standard — WHATWG