How Screen Readers Work
How Screen Readers Work
Screen readers convert content to speech or braille.
Screen Reader Types
| Screen Reader | Platform | Cost |
|---|---|---|
| NVDA | Windows | Free |
| JAWS | Windows | Paid |
| VoiceOver | macOS/iOS | Free |
| TalkBack | Android | Free |
| Narrator | Windows | Built-in |
How They Parse Content
- Document structure: Headings, landmarks, lists
- Semantic elements: Buttons, links, forms
- ARIA attributes: Roles, states, properties
- Text content: Labels, descriptions
- Relationships: Labels, descriptions
Common Screen Reader Issues
// ❌ Image without alt text
<img src="photo.jpg" />
// ✅ Image with alt text
<img src="photo.jpg" alt="Team meeting in conference room" />
// ❌ Icon without label
<button><Icon /></button>
// ✅ Icon with label
<button aria-label="Close dialog"><Icon /></button>
// ❌ Dynamic content without live region
<div>{statusMessage}</div>
// ✅ Dynamic content with live region
<div aria-live="polite">{statusMessage}</div>
Reading Order
// Reading order follows DOM order
function Layout() {
return (
<div>
<header>Header</header>
<main>Main Content</main>
<aside>Sidebar</aside>
<footer>Footer</footer>
</div>
);
// Screen reader reads: Header → Main Content → Sidebar → Footer
}
Live Regions
Live Regions
Announce dynamic content changes to screen readers.
Live Region Types
// Polite: Waits for user to finish
<div aria-live="polite">
{results.length} results found
</div>
// Assertive: Interrupts immediately
<div aria-live="assertive">
{errorMessage}
</div>
// Off: No announcement
<div aria-live="off">
{debugInfo}
</div>
Status Updates
function SearchResults({ query, results, isLoading }) {
return (
<div>
<input type="search" value={query} aria-label="Search" />
{/* Status announcement */}
<div aria-live="polite" className="sr-only">
{isLoading
? 'Loading results...'
: `${results.length} results for "${query}"`
}
</div>
{/* Visible results */}
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}
Error Announcements
function Form() {
const [errors, setErrors] = useState({});
return (
<form>
{/* Error summary for screen readers */}
{Object.keys(errors).length > 0 && (
<div role="alert" aria-live="assertive">
<h2>Please fix the following errors:</h2>
<ul>
{Object.entries(errors).map(([field, error]) => (
<li key={field}>{error}</li>
))}
</ul>
</div>
)}
{/* Form fields */}
</form>
);
}
Timer Updates
function Countdown({ seconds }) {
return (
<div>
{/* Don't announce every second */}
<div aria-live="off" aria-atomic="true">
{formatTime(seconds)}
</div>
{/* Announce when done */}
{seconds === 0 && (
<div role="status" aria-live="assertive">
Time's up!
</div>
)}
</div>
);
}
Best Practices
- Use politely for non-urgent updates
- Use assertively only for errors
- Don't overuse live regions
- Keep announcements concise
- Test with screen readers
Testing with Screen Readers
Testing with Screen Readers
Manual Testing
NVDA (Windows):
- Download from nvaccess.org
- Browse mode: Navigate with arrow keys
- Focus mode: Interact with forms
- Shortcut: Insert + Space to toggle modes
VoiceOver (macOS):
- Enable: Cmd + F5
- Navigate: VO + Arrow keys
- Interact: VO + Space
- Rotor: VO + U for navigation
Testing Checklist
- All images have meaningful alt text
- Headings create logical outline
- Form inputs have labels
- Errors are announced
- Interactive elements are keyboard accessible
- Focus order is logical
- Dynamic content is announced
Automated Testing
// axe-core testing
import { axe, toHaveNoViolations } from 'jest-axe';
describe('Accessibility', () => {
it('should have no violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
// Playwright screen reader testing
const { test, expect } = require('@playwright/test');
test('screen reader announcement', async ({ page }) => {
await page.goto('/');
// Check for aria-live region
const liveRegion = page.locator('[aria-live="polite"]');
await expect(liveRegion).toBeAttached();
// Perform action
await page.click('button');
// Check announcement
await expect(liveRegion).toContainText('Action completed');
});
Common Issues
| Issue | Impact | Fix |
|---|---|---|
| Missing alt text | Can't understand images | Add meaningful alt |
| No heading structure | Can't navigate | Use h1-h6 properly |
| Missing labels | Can't identify inputs | Add labels or aria-label |
| No focus indicator | Can't see focus | Add visible focus styles |
| Dynamic content silent | Miss updates | Use aria-live |
Practice Problems
Create a reusable React component implementing Screen Readers. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Screen Readers using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Screen Readers for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What is the difference between aria-live="polite" and "assertive"?
2. Which screen reader is free for Windows?
3. What is the primary purpose of Screen Readers?
4. What is a common mistake when implementing Screen Readers?
Flashcards
Question
What are live regions?
Click to reveal answer
Answer
ARIA attributes that announce dynamic content changes to screen readers.
Question
When should you use aria-live="assertive"?
Click to reveal answer
Answer
Only for urgent errors or messages that need immediate attention.
Question
How do screen readers navigate?
Click to reveal answer
Answer
By headings, landmarks, links, and form controls in the DOM order.
Question
What is the sr-only class?
Click to reveal answer
Answer
A CSS class that hides content visually but keeps it available to screen readers.
Question
What is Screen Readers?
Click to reveal answer
Answer
Screen Readers is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Screen readers convert content to speech or braille
- 2.Use aria-live for dynamic content updates
- 3.Test with actual screen readers, not just automated tools
- 4.Follow DOM order for reading order
- 5.Use sr-only class for screen reader only content
Interview Tips
- •Explain how screen readers work
- •Discuss live regions and when to use them
- •Know how to test accessibility with screen readers
Cheat Sheet
Screen Readers Cheat Sheet
Live Regions
- polite: Wait for user
- assertive: Interrupt
- off: No announcement
Testing
- NVDA: Free, Windows
- VoiceOver: Free, macOS
- Keyboard navigation
Common Issues
- Missing alt text
- No heading structure
- Missing form labels
- No focus indicator
- Silent dynamic content