Best Practices

AI Rules for Screen Reader Compatibility

AI generates visual-only UIs that screen readers cannot interpret. Rules for landmark regions, heading hierarchy, accessible names, reading order, dynamic announcements, and testing with NVDA and VoiceOver.

8 min read·April 8, 2025

Icon button announced as 'button' with no purpose — the entire page is one undifferentiated blob

Landmark regions, heading hierarchy, accessible names, reading order, NVDA and VoiceOver testing

AI Builds UIs That Screen Readers Cannot See

AI generates UIs with: no landmark regions (the entire page is one undifferentiated blob — no way to jump to navigation, main content, or sidebar), skipped heading levels (<h1> followed by <h4> — screen reader users think they missed content between h1 and h4), unnamed controls (icon buttons with no text or aria-label — announced as "button" with no indication of what it does), visual-only meaning (information conveyed through layout position, color, or icons with no text equivalent), and no dynamic announcements (content appears and disappears without screen reader notification).

Screen reader compatibility requires: landmark regions (<nav>, <main>, <aside>, <header>, <footer> — users navigate by landmarks), heading hierarchy (h1 through h6 in sequence — users navigate by headings), accessible names (every interactive element has a name — aria-label, aria-labelledby, or visible text), logical reading order (DOM order matches comprehension order), and dynamic announcements (aria-live regions notify of changes). AI generates none of these.

These rules cover: landmark regions for page structure, heading hierarchy for content navigation, accessible names for all controls, reading order that matches comprehension, dynamic announcements for live content, and testing methodology with NVDA and VoiceOver.

Rule 1: Landmark Regions for Page Structure

The rule: 'Wrap every major page section in a landmark element. <header>: site header with logo and navigation. <nav>: navigation links (label multiple navs: <nav aria-label="Main"> and <nav aria-label="Footer">). <main>: primary content (one per page). <aside>: sidebar or supplementary content. <footer>: site footer. <form>: search form or significant form sections. Screen reader shortcut: press D to jump between landmarks — the user navigates the page structure without reading every element.'

For landmark labeling: 'When multiple landmarks of the same type exist, label them: <nav aria-label="Main navigation"> and <nav aria-label="Breadcrumbs">. Without labels: screen readers announce "navigation" and "navigation" — the user cannot distinguish them. With labels: "Main navigation, navigation" and "Breadcrumbs, navigation" — each landmark is identifiable. The aria-label is the landmark name — it appears in the screen reader landmarks list and in rotor/elements list navigation.'

AI generates: <div className="header">, <div className="sidebar">, <div className="content"> — CSS class names that mean nothing to screen readers. The entire page is one landmark-less blob. Screen reader user: cannot jump to navigation, cannot skip to content, cannot find the sidebar. With semantic landmarks: the user presses D to cycle through header, nav, main, aside, footer. Each section is one keystroke away. CSS classes are for styling; landmark elements are for structure.

  • <header>, <nav>, <main>, <aside>, <footer> — not <div className='header'>
  • One <main> per page — the primary content area
  • Label multiple same-type landmarks: <nav aria-label='Main'> vs <nav aria-label='Footer'>
  • Screen reader D key: jump between landmarks — page structure in seconds
  • Landmarks appear in screen reader rotor/elements list — table of contents for the page
💡 Page Structure in Seconds

Without landmarks: screen reader reads every element sequentially from top to bottom. With landmarks: press D to jump header → nav → main → aside → footer. Each major section is one keystroke away. Landmarks are the table of contents for screen reader users.

Rule 2: Heading Hierarchy Without Skips

The rule: 'Use headings h1 through h6 in sequential order with no skipped levels. One <h1> per page (the page title). Sections under h1 use <h2>. Subsections under h2 use <h3>. Never: <h1> followed by <h4> (screen reader users think they missed h2 and h3 content). Never: headings for visual styling (<h3> used because it looks the right size — use CSS for sizing, headings for structure). Screen reader shortcut: H key to jump between headings — the heading hierarchy is the content outline.'

For the outline model: 'Headings create a document outline: h1: AI Rules for Keyboard Navigation -> h2: Rule 1: Tab Order -> h3: DOM Order Alignment -> h2: Rule 2: Focus Management -> h3: Modal Focus Trapping. Screen reader users: press H to navigate by headings, see the outline, and jump to the section they need. Skipped levels break the outline: h1 -> h4 implies there are h2 and h3 sections between them. The user searches for missing content that does not exist.'

AI generates: <h1>Page Title</h1><h3>Section</h3><h2>Another Section</h2><h4>Subsection</h4> — headings chosen by visual size, not hierarchy. The outline is nonsensical. Screen reader user navigating by headings: the structure does not match the content. They backtrack, re-read, and eventually give up. With proper hierarchy: the outline matches the content. Navigation by heading is efficient and predictable. One DOM restructuring, permanent improvement for screen reader users.

Rule 3: Accessible Names for All Controls

The rule: 'Every interactive element must have an accessible name — the text that the screen reader announces. Sources (in priority order): (1) aria-labelledby (references another element text), (2) aria-label (direct string label), (3) <label> element (for form inputs), (4) element text content (for buttons and links), (5) title attribute (last resort, not announced by all screen readers). If none of these provide a name: the screen reader announces "button" or "link" with no indication of purpose.'

For icon buttons: 'Icon buttons have no visible text: <button><svg>...</svg></button>. Screen reader: "button" (what button? no one knows). Fix: <button aria-label="Close"><svg>...</svg></button>. Screen reader: "Close, button." For icon links: <a href="/settings"><svg>...</svg></a>. Fix: <a href="/settings" aria-label="Settings"><svg>...</svg></a>. Every icon-only interactive element needs aria-label. If the icon has adjacent text: the text is sufficient, aria-label is not needed (avoid duplicate announcements).'

AI generates: <button><svg d="M6 18L18 6M6 6l12 12" /></button> — a close button with an X icon. Screen reader: "button." The user cannot tell if it closes, deletes, removes, or cancels. With aria-label="Close dialog": "Close dialog, button." The user knows exactly what the button does. One attribute transforms an unusable control into a clear, purposeful interaction for screen reader users.

  • Every interactive element needs a name: button, link, input, select, textarea
  • Icon buttons: aria-label='Close' or aria-label='Settings' — not just an SVG
  • Form inputs: <label htmlFor='email'>Email</label> — not placeholder as label
  • Priority: aria-labelledby > aria-label > <label> > text content > title
  • Test: screen reader should announce purpose of every control without visual context
⚠️ 'Button' vs 'Close Dialog, Button'

Icon button with no aria-label: screen reader announces 'button' — the user cannot tell if it closes, deletes, or cancels. With aria-label='Close dialog': 'Close dialog, button.' One attribute transforms an unusable control into a clear interaction.

Rule 4: Logical Reading Order

The rule: 'Screen readers read content in DOM order — which must match the logical reading order. If a card has: image, category badge, title, description, and a "Read more" link — the DOM order must be: title, category, description, image (or a logical sequence). CSS positions the image at the top visually, but the screen reader reads in DOM order. If the image is first in the DOM: screen reader announces the image alt text before the title, which makes no sense without the title context.'

For visually hidden content: 'Some content should be visible to screen readers but hidden visually: <span className="sr-only">External link, opens in new tab</span> after a link. The .sr-only class (position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0)) hides the text visually but screen readers read it. Use for: additional context that sighted users get from visual cues (icons, position, color) but screen reader users need as text.'

AI generates: CSS Grid with order property that places elements visually in one sequence but the DOM is in a completely different order. Screen reader users hear content in a jumbled sequence that does not match any visual or logical flow. With DOM-order-aligned layout: the screen reader reads content in the order it makes sense. CSS handles the visual arrangement; the DOM handles the reading order. Both are logical, independently.

Rule 5: Testing with NVDA and VoiceOver

The rule: 'Test with actual screen readers, not just automated tools. NVDA (free, Windows): the most commonly used screen reader. VoiceOver (built-in, macOS/iOS): used by Apple device users. Testing checklist: (1) Navigate by landmarks (D key in NVDA) — can you reach all major sections? (2) Navigate by headings (H key) — does the outline match the content? (3) Tab through all interactive elements — is every control named and operable? (4) Fill out a form — are labels announced, errors explained? (5) Interact with dynamic content — are updates announced via live regions?'

For testing frequency: 'Manual screen reader testing: quarterly for the full application, on every PR that adds new interactive components (modals, forms, tabs, menus). Automated testing (axe-core): every PR, catches 30-40% of issues. The gap: 60-70% of screen reader issues require manual testing — flow, context, announcement timing, and content order cannot be verified by automated tools. Five minutes with NVDA catches issues that hours of automated testing miss.'

AI generates: no screen reader testing. The first screen reader user is the first tester — they discover issues by being unable to use the product. With quarterly testing: issues are found proactively. Each new component is tested before merge. The screen reader experience is designed, not discovered. Five minutes of testing per component prevents hours of user frustration and support tickets.

ℹ️ Five Minutes Catches What Automation Misses

Automated testing catches 30-40% of screen reader issues. The other 60-70% — flow, context, announcement timing, reading order — require five minutes with NVDA. Manual testing per new component prevents hours of user frustration.

Complete Screen Reader Compatibility Rules Template

Consolidated rules for screen reader compatibility.

  • Landmark regions: header, nav, main, aside, footer — label multiples with aria-label
  • Heading hierarchy: h1 through h6 in sequence, no skips, one h1 per page
  • Accessible names: every control named — aria-label for icons, <label> for inputs
  • Reading order: DOM matches logical comprehension order, not visual layout order
  • sr-only for screen-reader-only text: context that visual users get from icons/color/position
  • Dynamic announcements: aria-live for updates, role='alert' for critical changes
  • NVDA testing (Windows): free, most common screen reader — test quarterly + new components
  • VoiceOver testing (Mac/iOS): built-in — test for Apple device compatibility