Back to Testing & Quality Assurance

screen-reader-testing

accessibilitya11yscreen readersweb testingassistive technologyWCAGUX
⭐ 36.8kπŸ“„ MITπŸ•’ 2026-06-16Source β†—

Install this skill

npx skills add wshobson/agents

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

Screen reader testing is a diagnostic process for web developers to verify how assistive technology interprets and communicates digital interfaces. This skill focuses on functional validation using industry-standard tools like NVDA, JAWS, and VoiceOver. By simulating the user journey of individuals who rely on non-visual browsing, you can identify failures in ARIA implementation, keyboard trap issues, and missing semantic context. Testing involves switching between virtual browse modes and focus-based interaction modes to ensure that dynamic content, form feedback, and navigation landmarks are accessible. This systematic approach ensures that interactive elements correctly announce state changes and that document structures remain logical for automated speech output.

When to Use This Skill

  • β€’Confirming that error messages are announced immediately after form validation
  • β€’Ensuring single-page application route changes notify users via document titles
  • β€’Testing complex widget accessibility for custom components
  • β€’Verifying that hidden SVG icons do not cause redundant speech output

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • β€œCheck accessibility for my form fields
  • β€œHow does my page sound in NVDA?
  • β€œValidate screen reader navigation order
  • β€œDebug why my dynamic alert isn't announcing
  • β€œAudit keyboard focus flow for screen readers

Pro Tips

  • πŸ’‘Always test with a minimum set of screen reader/browser combinations (NVDA+Firefox, VoiceOver+Safari) before broader testing.
  • πŸ’‘Focus on interactive elements, form fields, and dynamic content updates, as these are common areas for screen reader issues.
  • πŸ’‘Listen carefully to the spoken output and navigate exclusively with keyboard commands, mimicking a real screen reader user experience.

What this skill does

  • β€’Simulate screen reader navigation workflows
  • β€’Audit ARIA live region announcements
  • β€’Verify form label and error message associations
  • β€’Validate semantic document hierarchy and landmarks
  • β€’Test focus management within modals and widgets

When not to use it

  • βœ•When you need an automated compliance score (use an accessibility linting tool instead)
  • βœ•When testing mobile-specific touch gestures not covered by standard web readers

Example workflow

  1. Open the target application in the browser matching your primary screen reader
  2. Toggle the screen reader and navigate the page using only keyboard commands
  3. Use the rotor or elements list to verify heading levels and landmark distribution
  4. Enter form inputs and verify that labels and validation errors are announced correctly
  5. Trigger dynamic status updates to confirm they are read without interrupting current focus

Prerequisites

  • –Basic knowledge of HTML semantics and ARIA attributes
  • –Installed screen reader software (e.g., NVDA, JAWS, or VoiceOver)
  • –A standard web browser compatible with your chosen screen reader

Pitfalls & limitations

  • !Assuming all screen readers behave identically; NVDA and VoiceOver interpret DOM changes differently
  • !Relying solely on automated checks and missing manual focus-trapping issues
  • !Over-using ARIA labels when standard HTML elements would function better

FAQ

Why do I need to test on multiple screen readers?
Different screen readers use varying browser engines and logic, meaning your code might function perfectly in NVDA but fail in VoiceOver.
What is the difference between Browse and Focus mode?
Browse mode is for reading static content, while Focus mode is triggered when you interact with fields or buttons to capture specific key inputs.
How often should I perform manual screen reader testing?
Perform testing whenever you introduce new interactive components or modify your global navigation structure.

How it compares

This approach replaces vague accessibility audits with targeted, replicable command-line and manual interaction sequences, focusing on real-world behavior rather than theoretical compliance scores.

Source & trust

⭐ 37k starsπŸ“„ MITπŸ•’ Updated 2026-06-16
πŸ“„ Full skill instructions β€” original source: wshobson/agents
# Screen Reader Testing

Practical guide to testing web applications with screen readers for comprehensive accessibility validation.

## When to Use This Skill

- Validating screen reader compatibility
- Testing ARIA implementations
- Debugging assistive technology issues
- Verifying form accessibility
- Testing dynamic content announcements
- Ensuring navigation accessibility

## Core Concepts

### 1. Major Screen Readers

| Screen Reader | Platform | Browser | Usage |
| ------------- | --------- | -------------- | ----- |
| **VoiceOver** | macOS/iOS | Safari | ~15% |
| **NVDA** | Windows | Firefox/Chrome | ~31% |
| **JAWS** | Windows | Chrome/IE | ~40% |
| **TalkBack** | Android | Chrome | ~10% |
| **Narrator** | Windows | Edge | ~4% |

### 2. Testing Priority

Minimum Coverage:
1. NVDA + Firefox (Windows)
2. VoiceOver + Safari (macOS)
3. VoiceOver + Safari (iOS)

Comprehensive Coverage:
+ JAWS + Chrome (Windows)
+ TalkBack + Chrome (Android)
+ Narrator + Edge (Windows)


### 3. Screen Reader Modes

| Mode | Purpose | When Used |
| ------------------ | ---------------------- | ----------------- |
| **Browse/Virtual** | Read content | Default reading |
| **Focus/Forms** | Interact with controls | Filling forms |
| **Application** | Custom widgets | ARIA applications |

## VoiceOver (macOS)

### Setup

Enable: System Preferences β†’ Accessibility β†’ VoiceOver
Toggle: Cmd + F5
Quick Toggle: Triple-press Touch ID


### Essential Commands

Navigation:
VO = Ctrl + Option (VoiceOver modifier)

VO + Right Arrow Next element
VO + Left Arrow Previous element
VO + Shift + Down Enter group
VO + Shift + Up Exit group

Reading:
VO + A Read all from cursor
Ctrl Stop speaking
VO + B Read current paragraph

Interaction:
VO + Space Activate element
VO + Shift + M Open menu
Tab Next focusable element
Shift + Tab Previous focusable element

Rotor (VO + U):
Navigate by: Headings, Links, Forms, Landmarks
Left/Right Arrow Change rotor category
Up/Down Arrow Navigate within category
Enter Go to item

Web Specific:
VO + Cmd + H Next heading
VO + Cmd + J Next form control
VO + Cmd + L Next link
VO + Cmd + T Next table


### Testing Checklist

## VoiceOver Testing Checklist

### Page Load

- [ ] Page title announced
- [ ] Main landmark found
- [ ] Skip link works

### Navigation

- [ ] All headings discoverable via rotor
- [ ] Heading levels logical (H1 β†’ H2 β†’ H3)
- [ ] Landmarks properly labeled
- [ ] Skip links functional

### Links & Buttons

- [ ] Link purpose clear
- [ ] Button actions described
- [ ] New window/tab announced

### Forms

- [ ] All labels read with inputs
- [ ] Required fields announced
- [ ] Error messages read
- [ ] Instructions available
- [ ] Focus moves to errors

### Dynamic Content

- [ ] Alerts announced immediately
- [ ] Loading states communicated
- [ ] Content updates announced
- [ ] Modals trap focus correctly

### Tables

- [ ] Headers associated with cells
- [ ] Table navigation works
- [ ] Complex tables have captions


### Common Issues & Fixes

<!-- Issue: Button not announcing purpose -->
<button><svg>...</svg></button>

<!-- Fix -->
<button aria-label="Close dialog"><svg aria-hidden="true">...</svg></button>

<!-- Issue: Dynamic content not announced -->
<div id="results">New results loaded</div>

<!-- Fix -->
<div id="results" role="status" aria-live="polite">New results loaded</div>

<!-- Issue: Form error not read -->
<input type="email" />
<span class="error">Invalid email</span>

<!-- Fix -->
<input type="email" aria-invalid="true" aria-describedby="email-error" />
<span id="email-error" role="alert">Invalid email</span>


## NVDA (Windows)

### Setup

Download: nvaccess.org
Start: Ctrl + Alt + N
Stop: Insert + Q


### Essential Commands

Navigation:
Insert = NVDA modifier

Down Arrow Next line
Up Arrow Previous line
Tab Next focusable
Shift + Tab Previous focusable

Reading:
NVDA + Down Arrow Say all
Ctrl Stop speech
NVDA + Up Arrow Current line

Headings:
H Next heading
Shift + H Previous heading
1-6 Heading level 1-6

Forms:
F Next form field
B Next button
E Next edit field
X Next checkbox
C Next combo box

Links:
K Next link
U Next unvisited link
V Next visited link

Landmarks:
D Next landmark
Shift + D Previous landmark

Tables:
T Next table
Ctrl + Alt + Arrows Navigate cells

Elements List (NVDA + F7):
Shows all links, headings, form fields, landmarks


### Browse vs Focus Mode

NVDA automatically switches modes:
- Browse Mode: Arrow keys navigate content
- Focus Mode: Arrow keys control interactive elements

Manual switch: NVDA + Space

Watch for:
- "Browse mode" announcement when navigating
- "Focus mode" when entering form fields
- Application role forces forms mode


### Testing Script

## NVDA Test Script

### Initial Load

1. Navigate to page
2. Let page finish loading
3. Press Insert + Down to read all
4. Note: Page title, main content identified?

### Landmark Navigation

1. Press D repeatedly
2. Check: All main areas reachable?
3. Check: Landmarks properly labeled?

### Heading Navigation

1. Press Insert + F7 β†’ Headings
2. Check: Logical heading structure?
3. Press H to navigate headings
4. Check: All sections discoverable?

### Form Testing

1. Press F to find first form field
2. Check: Label read?
3. Fill in invalid data
4. Submit form
5. Check: Errors announced?
6. Check: Focus moved to error?

### Interactive Elements

1. Tab through all interactive elements
2. Check: Each announces role and state
3. Activate buttons with Enter/Space
4. Check: Result announced?

### Dynamic Content

1. Trigger content update
2. Check: Change announced?
3. Open modal
4. Check: Focus trapped?
5. Close modal
6. Check: Focus returns?


## JAWS (Windows)

### Essential Commands

Start: Desktop shortcut or Ctrl + Alt + J
Virtual Cursor: Auto-enabled in browsers

Navigation:
Arrow keys Navigate content
Tab Next focusable
Insert + Down Read all
Ctrl Stop speech

Quick Keys:
H Next heading
T Next table
F Next form field
B Next button
G Next graphic
L Next list
; Next landmark

Forms Mode:
Enter Enter forms mode
Numpad + Exit forms mode
F5 List form fields

Lists:
Insert + F7 Link list
Insert + F6 Heading list
Insert + F5 Form field list

Tables:
Ctrl + Alt + Arrows Table navigation


## TalkBack (Android)

### Setup

Enable: Settings β†’ Accessibility β†’ TalkBack
Toggle: Hold both volume buttons 3 seconds


### Gestures

Explore: Drag finger across screen
Next: Swipe right
Previous: Swipe left
Activate: Double tap
Scroll: Two finger swipe

Reading Controls (swipe up then right):
- Headings
- Links
- Controls
- Characters
- Words
- Lines
- Paragraphs


## Common Test Scenarios

### 1. Modal Dialog

<!-- Accessible modal structure -->
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc"
>
<h2 id="dialog-title">Confirm Delete</h2>
<p id="dialog-desc">This action cannot be undone.</p>
<button>Cancel</button>
<button>Delete</button>
</div>


// Focus management
function openModal(modal) {
// Store last focused element
lastFocus = document.activeElement;

// Move focus to modal
modal.querySelector("h2").focus();

// Trap focus
modal.addEventListener("keydown", trapFocus);
}

function closeModal(modal) {
// Return focus
lastFocus.focus();
}

function trapFocus(e) {
if (e.key === "Tab") {
const focusable = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
const first = focusable[0];
const last = focusable[focusable.length - 1];

if (e.shiftKey && document.activeElement === first) {
last.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === last) {
first.focus();
e.preventDefault();
}
}

if (e.key === "Escape") {
closeModal(modal);
}
}


### 2. Live Regions

<!-- Status messages (polite) -->
<div role="status" aria-live="polite" aria-atomic="true">
<!-- Content updates will be announced after current speech -->
</div>

<!-- Alerts (assertive) -->
<div role="alert" aria-live="assertive">
<!-- Content updates interrupt current speech -->
</div>

<!-- Progress updates -->
<div
role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Upload progress"
></div>

<!-- Log (additions only) -->
<div role="log" aria-live="polite" aria-relevant="additions">
<!-- New messages announced, removals not -->
</div>


### 3. Tab Interface

<div role="tablist" aria-label="Product information">
<button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
Description
</button>
<button
role="tab"
id="tab-2"
aria-selected="false"
aria-controls="panel-2"
tabindex="-1"
>
Reviews
</button>
</div>

<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
Product description content...
</div>

<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
Reviews content...
</div>


// Tab keyboard navigation
tablist.addEventListener("keydown", (e) => {
const tabs = [...tablist.querySelectorAll('[role="tab"]')];
const index = tabs.indexOf(document.activeElement);

let newIndex;
switch (e.key) {
case "ArrowRight":
newIndex = (index + 1) % tabs.length;
break;
case "ArrowLeft":
newIndex = (index - 1 + tabs.length) % tabs.length;
break;
case "Home":
newIndex = 0;
break;
case "End":
newIndex = tabs.length - 1;
break;
default:
return;
}

tabs[newIndex].focus();
activateTab(tabs[newIndex]);
e.preventDefault();
});


## Debugging Tips

// Log what screen reader sees
function logAccessibleName(element) {
const computed = window.getComputedStyle(element);
console.log({
role: element.getAttribute("role") || element.tagName,
name:
element.getAttribute("aria-label") ||
element.getAttribute("aria-labelledby") ||
element.textContent,
state: {
expanded: element.getAttribute("aria-expanded"),
selected: element.getAttribute("aria-selected"),
checked: element.getAttribute("aria-checked"),
disabled: element.disabled,
},
visible: computed.display !== "none" && computed.visibility !== "hidden",
});
}


## Best Practices

### Do's

- **Test with actual screen readers** - Not just simulators
- **Use semantic HTML first** - ARIA is supplemental
- **Test in browse and focus modes** - Different experiences
- **Verify focus management** - Especially for SPAs
- **Test keyboard only first** - Foundation for SR testing

### Don'ts

- **Don't assume one SR is enough** - Test multiple
- **Don't ignore mobile** - Growing user base
- **Don't test only happy path** - Test error states
- **Don't skip dynamic content** - Most common issues
- **Don't rely on visual testing** - Different experience

## Resources

- [VoiceOver User Guide](https://support.apple.com/guide/voiceover/welcome/mac)
- [NVDA User Guide](https://www.nvaccess.org/files/nvda/documentation/userGuide.html)
- [JAWS Documentation](https://support.freedomscientific.com/Products/Blindness/JAWS)
- [WebAIM Screen Reader Survey](https://webaim.org/projects/screenreadersurvey/)

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/screen-reader-testing/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/wshobson/agents/screen-reader-testing/SKILL.md
  • Cursor: ~/.cursor/skills/wshobson/agents/screen-reader-testing/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/wshobson/agents/screen-reader-testing/SKILL.md

πŸš€ Install with CLI:
npx skills add wshobson/agents

Read the Master Guide: Mastering Agent Skills β†’

Recommended Rules

View more rules β†’

Recommended Workflows

View more workflows β†’

Recommended MCP Servers

View more MCP servers β†’

Take It Further

Maximize your productivity with these powerful resources

πŸ“‹

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
πŸ“–

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid testing & quality assurance issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Testing & Quality Assurance and is published by W. Shobson, maintained in wshobson/agents.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.