How screen readers actually work
Screen readers (JAWS, NVDA, VoiceOver, TalkBack) don’t interpret pixels. They consume the accessibility tree—a semantic representation built from your HTML, ARIA attributes, and browser heuristics. From that tree they speak (or braille) each element’s role (what it is), name (what it’s called), and state (what it’s doing). If the tree is incomplete or misleading, users get silence, gibberish, or the wrong controls, even when the visual UI looks perfect.
The accessibility tree can diverge sharply from what sighted users see. A “beautiful” div that looks like a button is just a generic region unless it’s a real
<button> or a properly wired ARIA button with keyboard handlers. A label drawn as placeholder text remains invisible to assistive tech unless a programmatic
association exists. That mismatch is where modern websites typically break.
Where modern sites commonly fail screen readers
1) Missing or misleading accessible names
Icon-only buttons (cart, close, filter) often lack a programmatic name. Tooltips don’t count—screen readers need a label via text content, aria-label,
or an associated <label for>. When a control is only announced as “button,” users can’t tell what it does. Conversely, decorative text crammed into
aria-label creates verbosity that buries important information.
2) Divs masquerading as controls
Custom-styled divs are common for buttons, toggles, and tabs. Without the correct semantics and keyboard support, the screen reader can’t operate them.
Prefer native elements (<button>, <input>, <a>, <details>) or implement ARIA patterns completely:
role, name, state (aria-pressed, aria-selected), and handlers for Enter/Space/Esc and arrow keys where applicable.
3) Broken focus order and invisible focus
Screen reader users frequently navigate by keyboard. If the Tab sequence skips important controls, lands on hidden elements, or disappears behind sticky headers, tasks become impossible. Visual focus indicators must be obvious; “focus ring removed for aesthetics” remains a top failure.
4) Modals, dialogs, and off-canvas menus that trap or lose focus
Dialogs must receive focus on open, trap it within, and return focus to the trigger on close—all while being announced as a dialog with a meaningful title. Off-canvas menus often place focus at the end of the DOM or fail to expose a name (“Navigation”), leaving users stranded.
5) Single-page apps without announcements
In SPAs, route changes update content without a page load. Unless developers move focus to a logical heading and announce updates via aria-live,
screen readers may not signal that anything changed. Users remain in a “stale” context, unable to find the new content.
6) Form labels, errors, and instructions that aren’t programmatic
Placeholders are not labels. Each input needs a persistent label, and errors must be programmatically associated and announced. “There was an error” is useless; “Phone number must be 10 digits” tied to the field is actionable. After submit, focus should land on the first error.
7) Tables and data views with no structure
Data tables require true <th> headers, scope, and logical order. Grid-like product lists often read as a jumble of unrelated links unless
authored with semantic grouping and headings. Infinite scroll can disrupt reading order and focus without off-screen announcements and sensible landmarks.
8) ARIA overuse and anti-patterns
ARIA can help—but it can’t fix broken structure. Adding role="button" to a div without keyboard handlers creates a silent trap.
Over-declaring landmarks or nesting roles improperly floods users with noise. The rule of thumb: use native HTML when possible; add ARIA only for gaps.
9) Language and pronunciation issues
Screen readers rely on the page’s lang and inline language switches (e.g., lang="es") to pronounce words correctly.
Missing language attributes can render content unintelligible. Abbreviations and acronyms should be expanded at first mention.
10) Media and dynamic updates without alternatives
Videos need captions; audio needs transcripts. Status updates (cart count changes, saved states) should be conveyed with text or via polite live regions. Purely visual confirmations (“the checkmark turned green”) exclude non-visual users.
Practical tests you can run today
- Keyboard-only: Turn off the mouse. Can you open menus, operate sliders, fill forms, close dialogs, and complete checkout? Is focus always visible?
- Accessible names: Inspect key icons (cart, close, search, filter). Do they expose a name via the accessibility tree—not just a tooltip?
- SPA route change: Trigger a navigation. Does focus move to a meaningful heading? Is there a live announcement of the new view?
- Forms: Submit empty. Are errors specific, tied to fields, and announced? Does focus move to the first error?
- Modal: Open a dialog. Is the title announced? Can you tab within it and escape? Does focus return to the trigger on close?
For product teams: building access into your components
- Design system: Specify accessible names for icons, visible focus styles, and contrast tokens. Document keyboard flows for every interactive component.
- Frontend: Prefer native elements. When creating custom widgets, implement the full ARIA Authoring Practices pattern, not just the role.
- Content: Write persistent labels, concise alt text, and descriptive link text. Avoid “click here.”
- QA: Add screen reader smoke tests (NVDA/JAWS/VoiceOver) to acceptance criteria for shared components and checkout flows.
What barrier-driven harm looks like (and how to document it)
Harm occurs when a person is unable to access the same services others can—registering for a class, booking medical care, purchasing essentials, or managing an account. To turn that experience into actionable evidence, capture:
- Screenshots or short recordings that show the control and attempted action (e.g., activating the unlabeled “Place Order” button).
- Exact steps taken (keys pressed, controls activated), the URL, and the date/time.
- Assistive tech context: screen reader and browser (e.g., “NVDA + Firefox on Windows,” “VoiceOver on iPhone”).
- Consequence: missed deadline, inability to book/pay, loss of discount, or abandoned cart.
FAQs
“If we use a popular UI library, are we safe?”
Not automatically. Many libraries offer accessible primitives, but local overrides (removing focus rings, replacing labels with placeholders) reintroduce barriers. Verify each component’s semantics and keyboard flows in your implementation.
“Do we need ARIA everywhere?”
No. Start with correct HTML. Add ARIA only when a native element can’t express the needed behavior. Overuse creates noise and breaks expectations.
“Is testing with one screen reader enough?”
Different screen readers and browsers expose different quirks. A quick sweep—NVDA/Firefox, JA