Screen Readers in Practice

Web Accessibility — Screen Readers in Practice
Web Accessibility (a11y)
Chapter 7 · Screen Readers in Practice

🔊 Screen Readers in Practice

Chapters 2 through 6 covered techniques. This chapter is about the assistive technology actually consuming all of it — screen readers — and how to genuinely test with one instead of guessing.

The Accessibility Tree, Not Just the DOM

Browsers build a separate accessibility tree, derived from the DOM but filtered and transformed: decorative elements (aria-hidden, empty alt="") are removed, roles are computed by combining native semantics with any ARIA overrides (Chapters 2–3), and each element's accessible name is computed via a defined precedence order (aria-labelledby, then aria-label, then an associated <label>, then various fallbacks). This tree — not the visual DOM/CSSOM a sighted user's browser renders — is literally what a screen reader reads from. Every previous chapter has really been about correctly shaping this tree, not just "making things look right."

DOM / CSSOMAccessibility Tree
What renders visuallyWhat a screen reader announces
Includes every elementDecorative/hidden elements filtered out
Visual styling determines appearanceComputed roles and accessible names determine announcement

Testing With a Real Screen Reader

VoiceOver (built into macOS/iOS, free) and NVDA (free, Windows) are the standard tools. Browser DevTools' accessibility panel shows a useful static snapshot of a computed name or role — but it's not a substitute for actually navigating with a screen reader turned on.

Snapshot vs Real Session

DevTools Panel Only

Confirms an individual element's computed name/role looks correct in isolation — misses reading-order weirdness and awkward interaction flow entirely.

Real Screen Reader Session

Surfaces redundant or verbose announcements, confusing reading order, and interaction flows that look fine one element at a time but sound confusing in continuous use.

👁️‍🗨️ Visually-Hidden Text

A CSS class that hides content visually while keeping it in the accessibility tree — critically different from display: none or visibility: hidden, both of which remove content from the tree entirely:

The Standard Visually-Hidden Pattern

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; /* prevents wrapping from expanding the clip box */ }

Used to add context sighted users get visually but a screen reader user tabbing through link names alone would miss entirely:

<a href="/policy">Read more<span class="visually-hidden"> about our return policy</span></a> // Sighted users see "Read more" in context; a screen reader announces // "Read more about our return policy" — not three identical "Read more"s

📢 aria-live Regions, Revisited

Value / RoleBehavior
aria-live="polite" / role="status"Waits for a pause, doesn't interrupt current speech
aria-live="assertive" / role="alert"Interrupts immediately — reserve for genuinely urgent information
<div role="status">12 results found</div>

💻 Coding Challenges

Challenge 1: Add Visually-Hidden Context to Repeated Links

A product listing page has three "View details" links, one per product ("Blue Sneakers," "Red Jacket," "Green Hat"). Rewrite each link so a screen reader announces which product it belongs to, without changing the visible text.

Goal: Practice the exact repeated-link-context pattern this chapter describes.

→ Solution

Challenge 2: Choose Polite or Assertive

For each of (a) a "Your changes have been saved" confirmation after a routine form edit, and (b) a "Your session is about to expire in 10 seconds" warning, choose aria-live="polite" or aria-live="assertive" and justify each.

Goal: Practice applying the urgency distinction to two realistically different cases.

→ Solution

Challenge 3: Explain Why a DevTools Check Wasn't Enough

A developer confirms every individual element's accessible name looks correct in the DevTools accessibility panel, then ships a page that real screen reader users report as confusing to navigate. Explain what kind of problem this scenario likely represents.

Goal: Practice articulating the gap between a per-element snapshot and a real, continuous screen reader session.

→ Solution

⚠️ Gotcha: Overusing aria-live="assertive"

It's tempting to reach for aria-live="assertive" (or role="alert") for routine updates — a "form saved" confirmation, a "3 items in cart" counter update — because it feels like the more attention-grabbing, "important" choice. It isn't harmless: assertive interrupts whatever the screen reader was already announcing, which is disruptive and disorienting when used for anything that isn't genuinely urgent. Most updates belong under aria-live="polite" (or role="status") — reserve assertive/alert strictly for the rare cases that truly warrant interrupting the user, like a critical error or a security warning.

🎯 What's Next

With the assistive technology itself understood, the next chapter puts everything together into real, working components: Accessible Components & Patterns — building a genuinely correct modal, dropdown/combobox, and tabs.