Concept · The browser
The accessibility tree and ARIA
What a screen reader actually sees: roles, names and states — and why native elements beat ARIA.
Alongside the DOM, the browser builds an accessibility tree: a simplified tree that screen readers, voice control and other assistive technology read instead of your pixels. Each node has:
- a role: button, link, heading (level 2), checkbox, textbox, navigation…
- an accessible name: what it's called ("Delete invoice", "Search");
- states and properties: checked, expanded, disabled, invalid, current page…
A screen reader announces those three things: "Delete invoice, button." If they're missing or wrong, that user gets a broken page, even when it looks perfect.
Native HTML fills it in for you
<!-- Role: button. Name: "Save". Focusable, Enter/Space activate it. -->
<button type="button">Save</button>
<!-- Role: none. Name: none. Not focusable. Keyboard does nothing. -->
<div class="btn" onclick="save()">Save</div>Semantic elements like <button>, <a href>, <label>, <nav>, <main>, <h1>–<h6>, <ul> and <dialog> give you correct roles, names and keyboard behaviour for free.
Where the name comes from
In order of precedence: aria-labelledby, then aria-label, then native labelling (<label for>, alt, <caption>), then text content, then title. Common bugs:
- An icon-only button with no name. Add
aria-label="Close"or visually hidden text. - An input with only a
placeholder. Placeholders aren't reliable labels, so use a<label>. - A link whose name is "click here." Names should make sense read out of context.
ARIA: a repair kit
ARIA attributes change what the accessibility tree says without changing behaviour. role="button" on a <div> makes a screen reader announce a button, but the div still can't be focused or activated with the keyboard until you add tabindex="0" and key handlers yourself.
The first rule of ARIA: if a native element does it, use that instead. Reach for ARIA when HTML has no equivalent:
aria-expanded,aria-controlsfor disclosure widgets and menus;aria-live="polite"to announce updates like "3 results found";aria-current="page"on the active nav link;aria-hidden="true"on decorative icons.
Wrong ARIA is worse than none. A role that doesn't match the element's behaviour misleads people who trust it.
Checking it
Open the Accessibility panel in your browser's dev tools to see the tree. Testing Library's getByRole('button', { name: 'Save' }) queries the same tree, so tests that use it double as accessibility checks.