What a screen reader gets from your HTML and ARIA
How the browser turns HTML and ARIA into the accessibility tree a screen reader reads, and where each role, name, description and state in it comes from.
In short: The browser builds an accessibility tree from the DOM: an object for each part of the page, with a role, a name, a description, states and relations, handed to the operating system's accessibility API, where the screen reader reads it. HTML fills the tree on its own; ARIA can change what goes into it, but never what the page does.
01
When you need this
A tester's notes on a recreated settings page come back with three lines:
- The bin icon is read as "button", and nothing more.
- "Email alerts" is read as plain text. Nothing says it can be switched, or whether it is on.
- The hint "Shown on your profile" under the Display name field is never read.
On screen all three look right, and the code holds every word the tester missed. A screen reader doesn't read the code. It reads what the browser hands it, and that is what this page is about.
02
From the DOM to the screen reader
What the person hears passes through four layers:
- The DOM: the elements and attributes your HTML and script produce.
- The accessibility tree: "User agents acquire information from the DOM and create a parallel structure called the accessibility tree, made up of accessible objects" (AccName 1.1 §1).
- The platform accessibility API: the browser exposes each object through the operating system's accessibility API, and fires an event when one changes.
- The assistive technology: a screen reader, voice control or a magnifier asks the API what is there and what changed, and turns it into speech, braille or a command.
Core Accessibility API Mappings (Core-AAM) 1.2 §1.1 lists the APIs browsers map to:
| Platform | Accessibility API |
|---|---|
| Windows | MSAA with IAccessible2, and UI Automation |
| macOS | macOS Accessibility Protocol (AX API) |
| Linux | ATK and AT-SPI |
| Android | Android accessibility API |
iOS is not in that list. Each object in the tree carries the same kinds of information, whichever API it reaches:
| What | Example | Where it comes from |
|---|---|---|
| Role | button, checkbox, link | The element, or a role attribute |
| Name | "Delete draft" | Content, a label, alt, aria-label or aria-labelledby |
| Description | "Shown on your profile" | aria-describedby, or title when it isn't the name |
| States and properties, the value among them | checked, expanded, disabled; a slider's value | The element's own state, or aria-* attributes |
| Relationships | labelled by, controls, owns; parent and child | label for, aria-labelledby, aria-controls, aria-owns, nesting |
Some content never reaches the tree. WAI-ARIA 1.2 counts an element as hidden when it, or an ancestor, is not rendered (display: none) or has aria-hidden="true", and AccName 1.2 lists visibility: hidden as hidden too. Text at opacity: 0, or moved off screen, is not hidden: browsers still expose it. So a badge inside an aria-hidden link leaves the tree with the link, as visible text that screen readers skip shows, while links moved off screen and hidden with aria-hidden still take focus, as aria-hidden content that keyboard focus still reaches shows.
03
Roles: what HTML gives, and what ARIA changes
Every HTML element has an implicit role, the one it maps to with no role attribute. HTML Accessibility API Mappings (HTML-AAM) §3.5 sets them out:
| Element | Implicit role |
|---|---|
<button> |
button |
<a href="…"> |
link |
<a> with no href |
generic |
<input type="checkbox"> |
checkbox, with its checked state |
<nav> |
navigation |
<section> |
region when it has an accessible name, generic otherwise |
<div>, <span> |
generic |
A role attribute replaces the implicit role: ARIA lets an element "be understood as a particular widget or structural type regardless of any semantics inherited from the implementing host language" (WAI-ARIA 1.2 §1.1). WAI-ARIA 1.2 §5.3 sorts its roles into six categories:
| Category | Roles, for example | What it means for authors |
|---|---|---|
| Abstract | command, composite, input, landmark, range, roletype, section, sectionhead, select, structure, widget, window | "Authors MUST NOT use abstract roles in content." They exist to build the model |
| Widget | button, checkbox, option, slider, switch, tab, textbox; and the composites combobox, grid, listbox, menu, radiogroup, tablist, tree | Interactive: the page provides the keys and the behaviour |
| Document structure | article, generic, heading, img, list, none, table, toolbar, tooltip | Organises content; not usually interactive |
| Landmark | banner, complementary, contentinfo, form, main, navigation, region, search | Regions people move between |
| Live region | alert, log, marquee, status, timer | Content whose changes are announced |
| Window | alertdialog, dialog | A window inside the page |
The aria-* attributes are states and properties. WAI-ARIA 1.2 §6.1 says properties such as aria-labelledby are "often less likely to change" than states such as aria-checked, which "may change frequently due to user interaction", and adds that this "is not a rule; a few properties, such as aria-valuetext are expected to change often". The specification mostly calls both "attributes".
None of this changes what the element does. "Aside from using WAI-ARIA markup to improve what is exposed to accessibility APIs, user agents behave as they would natively" (WAI-ARIA 1.2 §1.3). Take this:
<div role="button">Save changes</div>The tree now holds a button named "Save changes". The div still takes no focus, and Enter and Space still do nothing, because focus and keys come from the element, tabindex and your script, never from ARIA. Div buttons that can't be reached with Tab shows the rest of that story.
04
The rules of ARIA use
The W3C document Using ARIA set out rules for using ARIA in HTML. Its last Working Draft, of 27 September 2018, had five. On 24 February 2026 W3C published it as a Discontinued Draft, which keeps four rules "for historical purposes and for easier reference" and points to the ARIA Authoring Practices Guide for further guidance. It is informative only: advice, not a requirement.
| Rule | What it says | Where it shows |
|---|---|---|
| 1 | Use a native HTML element or attribute with the semantics and behaviour you need, instead of adding ARIA to another element | Div buttons that can't be reached with Tab |
| 2 | Don't change native semantics unless you really have to: <div role="tab"><h2>…</h2></div>, not <h2 role="tab"> |
Using ARIA's own example; WAI-ARIA 1.2 gives a tab presentational children (§5.2.9), so browsers should not expose the h2 as a heading |
| 3 | All interactive ARIA controls must be usable with the keyboard | Arrow keys inside a widget: roving tabindex or aria-activedescendant |
| 4 | Don't use role="presentation" or aria-hidden="true" on a focusable element |
aria-hidden content that keyboard focus still reaches |
| 5 (2018 draft only) | All interactive elements must have an accessible name | Icon-only buttons with no accessible name |
The fifth rule is not in the discontinued version, but the need it described has not gone: WCAG 2.2 success criterion 4.1.2 requires a name for every user interface component.
05
Where the name and the description come from
The browser works out the accessible name with the Accessible Name and Description Computation (AccName). Version 1.1, of 18 December 2018, is the W3C Recommendation; version 1.2 is a Working Draft, dated 21 October 2026 at this page's date. Both take the first source that gives a name, in this order (AccName 1.1 §4.3; the step for a control embedded in a label is left out here):
- Hidden: a hidden element gives no name, unless
aria-labelledby,aria-describedbyor alabelpoints at it. aria-labelledby: the text of each element it points to, in order. An id that isn't on the page is skipped, as invalid ARIA that misleads screen readers shows.aria-label: its value. It replaces the text inside the element as the name, which is how a button can show "Add to bag" and be named something else, as in buttons and links named differently from their text.- The element's own label: a
labelelement,alt, a table'scaption. - Content: for roles that take their name from content, among them button, link, checkbox, option and tab, the text inside, child by child, with CSS
::beforeand::aftertext. A button holding only an SVG with no title has none, as in icon-only buttons with no accessible name. - Tooltip:
title, only when nothing above gave a name.
HTML-AAM §4.1 gives each element its own order within those steps. For a text field: aria-labelledby or aria-label, then its label, then title, then placeholder. For a button: aria-labelledby or aria-label, then a label, then its content, then title. For an img: aria-labelledby or aria-label, then alt, then title.
Some roles can't be named at all. WAI-ARIA 1.2 §5.2.8.6 lists them, generic and paragraph among them, and AccName 1.2 says authors "MUST NOT use the aria-label or aria-labelledby attributes" on them. That is why an aria-label on a plain span may never reach the screen reader, as aria-label on divs and spans that screen readers often skip shows.
The accessible description is a separate property. AccName 1.1 §4.2 builds it from the elements aria-describedby points to. AccName 1.2 and HTML-AAM §4.2, both drafts, add a fuller order: aria-describedby, then aria-description, then features such as a table's caption when it isn't already the name, then title when it isn't already the name. aria-description is not in WAI-ARIA 1.2. When each screen reader reads a description is a question of support, not of the tree: see screen reader support. Names and descriptions compared sets the naming attributes side by side.
06
Reading the tree in developer tools
Both browsers below show the accessibility tree they build from the page. Firefox's inspector shows its own tree, the same on every platform, not the platform API's view of it.
- Chrome: in the Elements panel, select an element and open the Accessibility tab (it may be behind the More Tabs button). It shows the element's place in the accessibility tree, its ARIA Attributes and its Computed Properties. Turn on "Show accessibility tree" to replace the DOM tree with the full-page accessibility tree.
- Firefox: open the Accessibility tab in the developer tools, or right-click the page and choose Inspect Accessibility Properties. The left panel lists each object's role and name; the right panel shows its name, role, actions, value, description, states and relations, among others. Check for issues filters the tree to Contrast, Keyboard or Text Labels problems, and Show Tabbing Order numbers the focusable items.
Browsers word roles their own way: Firefox lists a button as "pushbutton". For each control, read the role, the name, the description and the states, and check each against what the screen shows. The tree says whether the information is there. It doesn't say what a screen reader does with it, which depends on the screen reader, its mode and its version; free accessibility tools lists more ways to look.
07
Worked example
The settings page from the tester's notes, as built:
<div class="toggle on" onclick="toggleAlerts()">Email alerts</div>
<button class="icon-button" onclick="deleteDraft()">
<svg class="icon">
<use href="#bin"></use>
</svg>
</button>
<input id="display-name" placeholder="Display name">
<p class="hint">Shown on your profile</p>Reading each control in the developer tools, then fixing it:
| Control | In the tree, as built | In the tree, after the fix |
|---|---|---|
| Email alerts | A generic div, whose role can't be named; the words are plain text; no state, and no focus |
A checkbox named "Email alerts", checked |
| Bin icon | A button with no name: the SVG has no title, so the content gives nothing | A button named "Delete draft" |
| Display name | A text field named "Display name", from the placeholder; no description | A text field named "Display name", from its label, described as "Shown on your profile" |
The fixed markup:
<input type="checkbox" id="alerts" checked>
<label for="alerts">Email alerts</label>
<button type="button" aria-label="Delete draft" onclick="deleteDraft()">
<svg class="icon" aria-hidden="true">
<use href="#bin"></use>
</svg>
</button>
<label for="display-name">Display name</label>
<input id="display-name" aria-describedby="display-name-hint">
<p id="display-name-hint">Shown on your profile</p>The switch became a native checkbox, so its role, its state and its keys all come from the browser (rule 1). The bin button got a name, and its icon left the tree. The field got a visible label, which is now its name, so the name no longer rests on a placeholder that leaves the screen once someone types; its hint became its description. Reading the tree again, each control now has the role, name, state and description the tester expected. A screen reader test then confirms how each one is heard.
08
Common mistakes
- Adding a role and stopping there.
role="button"puts a button in the tree, but the element still takes no Tab and answers no key, since ARIA changes only the tree. See div buttons that can't be reached with Tab. - Naming a
divorspanwitharia-label. Generic is a role that can't be named, so the words may not reach the screen reader. See aria-label on divs and spans that screen readers often skip. - Putting
aria-hidden="true"on something focus can reach. It leaves the tree but keeps its Tab stop, so focus lands on nothing the screen reader can name. See aria-hidden content that keyboard focus still reaches. - Pointing
aria-labelledbyat an id that isn't on the page, or writingaria-role. The browser skips a reference it can't follow and an attribute it doesn't know, and the source still looks right. See invalid ARIA that misleads screen readers. - An
aria-labelthat drops the visible words. It comes before the content in the order, so the words on the button are no longer part of its name. See buttons and links named differently from their text. - An icon with no text. For a button, content is the last source before
title, and an SVG with no title gives none. See icon-only buttons with no accessible name. - Putting information inside a hidden element. Everything inside
aria-hidden="true"leaves the tree with it. See visible text that screen readers skip. - Using an abstract role, such as
role="widget". WAI-ARIA forbids it, and HTML-AAM §3.5.1 says browsers "MUST ignore any abstract or invalid role token". See the HTML and ARIA reference for the roles to use. - Relying on
titleorplaceholderas the name. HTML-AAM uses them only when nothing else names the field, and a placeholder leaves the screen once someone types. See form fields with missing or mismatched labels.
09
What the standard says
- WCAG 2.2, success criterion 4.1.2 Name, Role, Value (Level A), required: "For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies." EN 301 549 V3.2.1 has the same requirement as clause 9.4.1.2; JIS X 8341-3:2016, which is WCAG 2.0, has it as 4.1.2; and Section 508 (E205.4) requires WCAG 2.0 Level A and AA, which includes it.
- WAI-ARIA 1.2 (W3C Recommendation, 6 June 2023), required of content that uses ARIA: "Authors MUST NOT use abstract roles in content" (§5.3.1). On
aria-hidden: "Authors using aria-hidden to hide visible content from screen readers MUST ensure that identical or equivalent meaning and functionality is exposed to assistive technologies." §1.3 and §6.1 explain the model rather than require anything. - AccName 1.1 (W3C Recommendation, 18 December 2018), required of browsers: they "MUST compute an accessible name" with its algorithm (§4.1). AccName 1.2 is a Working Draft, and the W3C's publication history shows whether a newer version has been published.
- HTML-AAM 1.0 (Working Draft, 26 October 2026) and Core-AAM 1.2 (Candidate Recommendation Draft, 30 October 2026): what browsers map, not what authors write. Core-AAM 1.1 is the latest Core-AAM Recommendation.
- Using ARIA (Discontinued Draft, 24 February 2026): advice only, and no longer maintained.
10
Limits
This page covers what reaches the tree. It doesn't settle:
- What each screen reader says, and in which mode. That is a question of support, tested combination by combination; EaseWeb's results are on screen reader support.
- Keys inside widgets: see arrow keys inside a widget: roving tabindex or aria-activedescendant.
- What each element, role and attribute does: see the HTML and ARIA reference.
- Differences between browsers and versions. Trees differ, and the mappings above are still drafts in places, so record the browser and version you read the tree in.
Whether a control's name or description is the right one for its users is the tester's call, made on the page itself.
11
Terms
- accessibility tree
- The tree of accessible objects a browser builds from the DOM for assistive technology, each with a role, a name, states and relations.
- accessibility API
- The interface an operating system gives assistive technology for reading and operating the parts of an application, such as UI Automation on Windows.
- accessible name
- The short text that identifies an object in the accessibility tree, such as a button's label.
- accessible description
- Extra text about an object, kept apart from its name, such as the hint under a field.
- implicit role
- The role an HTML element has with no role attribute, as HTML-AAM maps it: a button element is a button, a nav element is navigation.
- abstract role
- A role WAI-ARIA uses only to build its model of roles, such as widget or input; authors must not use one in content.
12
Sources and guides
- Advice WAI-ARIA 1.2: 1.3 User Agent Support (opens in a new tab)
- Required WAI-ARIA 1.2: 5.3 Categorization of Roles (opens in a new tab)
- Required WAI-ARIA 1.2: 6.1 Clarification of States versus Properties (opens in a new tab)
- Required Accessible Name and Description Computation 1.1: 4.3 Accessible Name and Description Computation (opens in a new tab)
- Advice Accessible Name and Description Computation 1.2 (Working Draft): 4.2 Description Computation (opens in a new tab)
- Advice HTML Accessibility API Mappings 1.0 (Working Draft): 3.5 HTML Element Role Mappings (opens in a new tab)
- Advice HTML Accessibility API Mappings 1.0 (Working Draft): 4.1 Accessible Name Computations By HTML Element (opens in a new tab)
- Advice Core Accessibility API Mappings 1.2 (Candidate Recommendation Draft): 1.1 Accessibility APIs (opens in a new tab)
- Advice Using ARIA (Discontinued Draft): 2. Notes on ARIA Use in HTML (opens in a new tab)
- Required WCAG 2.2: 4.1.2 Name, Role, Value (opens in a new tab)
Guides that show it
- Div buttons that can't be reached with Tab A pointer-only control prevents keyboard users from saving their work.
- Icon-only buttons with no accessible name The header's search, basket and menu buttons are drawn with SVG icons and contain no words, so each is announced only as "button".
- Buttons and links named differently from their text Each "Add to bag" button got an aria-label naming the product and saying "cart", so its name lacks the words on the button and voice control can't find it.
- Invalid ARIA that misleads screen readers A gift-wrap checkbox points aria-labelledby at a renamed label id, or writes its role as aria-role, so a screen reader hears a nameless box or no checkbox.
- aria-label on divs and spans that screen readers often skip A product list puts star ratings and a free-delivery icon in spans with an aria-label, but a plain span can't be named, so many screen readers say nothing.
- aria-hidden content that keyboard focus still reaches A closed slide-out menu is moved off screen and given aria-hidden="true", which hides its links from screen readers but leaves them all in the tab order.
- Visible text that screen readers skip A shop draws its Sold out badge inside a product photo link that is hidden from screen readers, so a screen reader never says which mug is sold out.
This page explains a practice, with recreated examples. It is not a screen reader test result, and it is not a finding about a named client.