Part IV β The Reach Β· Lesson 8.3
Screen Reader
In one lineSemantics decide what gets announced β ARIA patches the gaps, and wrong ARIA is worse than none.
Where you've seen itA row of icon-only buttons that all announce as "button, button, button".
Diagram
DOM β ACCESSIBILITY TREE β what the screen reader actually reads
DOM ACCESSIBILITY TREE
βββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
<button> name: "Delete invoice 42"
<svg aria-hidden="true"> βββΊ role: button
<span class="sr-only"> state: enabled
Delete invoice 42
</span>
</button>
<div onclick>π</div> βββΊ (nothing β not in the tree)
Every element contributes: NAME Β· ROLE Β· STATE Β· VALUEWHERE THE NAME COMES FROM β first match wins
1. aria-labelledby="id" points at visible text β preferred
2. aria-label="Delete" invisible string
3. <label for="β¦"> form controls
4. text content <button>Save</button> β simplest
5. title attribute unreliable, don't rely on itTHE FIVE ARIA RULES THAT MATTER
1. Don't use ARIA if HTML can do it <button> > role="button"
2. Don't change native semantics β <h2 role="button">
3. All interactive ARIA must be keyboard role="button" needs keys (8.2)
operable
4. Don't hide focusable elements β aria-hidden on a tab stop
5. Every control needs an accessible nameLIVE REGIONS β announcing things that change
aria-live="polite" waits for a pause search results, saved
aria-live="assertive" interrupts NOW errors, session expiry
role="status" = polite "3 items added"
role="alert" = assertive "Payment failed"
The region must exist in the DOM BEFORE the text changes,
or nothing is announced.How it works
- Start semantic. Headings in order, landmarks (
main,nav,aside), lists as lists, and real form controls. - Name every control. Icon-only buttons need
aria-labelor visually hidden text;aria-labelledbyis better when visible text already exists. - Hide decoration with
aria-hidden="true"on icons β but never on anything focusable. - Announce dynamic changes through a live region that's already mounted; toggling the region itself into existence announces nothing.
- Test with a real screen reader: VoiceOver (Cmd+F5) on macOS, NVDA on Windows. Fifteen minutes teaches more than any checklist.
<button aria-label="Delete invoice 42">
<svg aria-hidden="true">β¦</svg>
</button>
<div role="status" aria-live="polite" class="sr-only">
<!-- mounted always; text swapped in when results change -->
</div>Trade-offs
| Technique | When |
|---|---|
| Visible text | Always best β helps everyone |
aria-labelledby | Visible text exists elsewhere |
aria-label | Icon-only controls |
sr-only text | Extra context for a visual pattern |
role="alert" | Genuine errors only β it interrupts |
Interview angle
"How do you make a custom dropdown screen-reader friendly?"
- Use
<select>if the design allows; the platform version is correct everywhere for free. - If not:
role="combobox"witharia-expanded,aria-controls,aria-activedescendant, plus the full arrow/Enter/Escape key contract. - Announce results with a polite live region, and verify in VoiceOver and NVDA β the specs agree, the implementations don't always.
Recap
- Name, role, state, value β every control needs all four.
- Semantic HTML gives them free; ARIA only patches genuine gaps.
- Live regions must exist before their content changes.