Accessible Structure — What a Screen Reader Hears
IntermediateA screen reader builds its own picture of your page from the accessibility tree. Understanding what that tree contains explains why semantic markup is not a formality.
Overview
Accessibility becomes concrete once you know what a screen reader actually does. It does not read the page top to bottom — users navigate by headings, landmarks, links and form fields, jumping between them. That navigation is built from the accessibility tree, which the browser derives from your HTML: each element contributes a role, a name, a state and a value. A div contributes none of those, which is why a page of divs offers nothing to navigate by. Every accessibility technique after this one is about filling that tree correctly.
The Accessibility Tree
Role, name, state, value — the four things assistive technology reads.
<button aria-pressed="true">Bookmark</button>
// role: button name: "Bookmark" state: pressed
<input type="checkbox" id="pro" checked>
<label for="pro">Pro only</label>
// role: checkbox name: "Pro only" state: checked
<div onclick="…">Bookmark</div>
// role: generic name: (none) state: (none)
// Announced as nothing. Not reachable. Not operable.
/* Inspect it: DevTools -> Elements -> Accessibility panel shows the
computed role and name for the selected element. If the name is
empty or the role is "generic" on something interactive, that is
your bug, visible before any screen reader is involved. */
/* The accessible NAME is computed in a fixed priority order:
aria-labelledby > aria-label > the native label / content >
title attribute
So an aria-label silently overrides visible text — which is why a
button reading "Save" can announce something completely different. */
<button aria-label="Close">Save</button> // announces "Close". Bug.How Users Navigate
The shortcuts that make semantic markup pay off.
/* Screen-reader users rarely read linearly. They:
- press H to jump between headings
- press D or use a landmarks menu to jump between regions
- open a list of every LINK on the page
- open a list of every FORM FIELD
- press T to move between tables
Every one of those lists is built from your elements. A page of
divs offers empty lists. */
/* Which is why these three matter more than any ARIA attribute:
1. a correct heading outline
2. landmarks (header, nav, main, aside, footer)
3. link and button text that makes sense out of context */
/* A skip link — the first focusable element on the page, so a
keyboard user can bypass the navigation on every single page */
<a href="#main" class="skip-link">Skip to content</a>
<main id="main" tabindex="-1">
.skip-link {
position: absolute; inset-block-start: -3rem;
background: var(--surface); padding: .5rem 1rem;
}
.skip-link:focus { inset-block-start: 0 } /* visible only on focus */
/* tabindex="-1" on the target makes focus actually move there, not
just the scroll position. */Hiding Things Correctly
Four ways to hide something, each with different consequences.
/* Hidden from everyone */
display: none
visibility: hidden
<div hidden>
/* Hidden visually, still announced — for screen-reader-only text */
.sr-only {
position: absolute; width: 1px; height: 1px;
padding: 0; margin: -1px; overflow: hidden;
clip-path: inset(50%); white-space: nowrap; border: 0;
}
<button><svg aria-hidden="true"/><span class="sr-only">Delete</span></button>
/* Visible, but hidden from screen readers — for decorative content
whose meaning is already conveyed in text */
<svg aria-hidden="true">
<span aria-hidden="true">→</span>
/* NEVER put aria-hidden on something focusable: it creates a
control a screen-reader user can tab to but cannot perceive. */
<button aria-hidden="true">Save</button> // a trap
/* Hiding a whole inert region behind a modal */
<div inert>…</div> // unfocusable AND unreachable, in one attribute
/* The common mistake: hiding a mobile menu with display: none but
leaving its links tabbable in a version hidden with opacity or
visibility — the user tabs into invisible links. */Key Points to Remember
- 1Assistive technology reads a role, name, state and value for each element — a div supplies none of them
- 2The accessible name has a priority order, so aria-label silently overrides visible text
- 3Users navigate by headings, landmarks, links and form fields rather than reading linearly
- 4A skip link as the first focusable element lets keyboard users bypass navigation on every page
- 5.sr-only hides visually but keeps content announced; aria-hidden on a focusable element creates an unusable control
Interview Questions
Sign in to ask AriaWhat is the accessibility tree and how does it relate to your HTML?
How do screen-reader users typically navigate a page?
What is the difference between display: none, aria-hidden and a .sr-only class?
Ask Aria about Accessible Structure — What a Screen Reader Hears
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.