CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/screen-reader-test-author

Builds the full manual-accessibility artifact surface: step-by-step screen-reader test scripts for NVDA (Windows), JAWS (Windows), VoiceOver (macOS / iOS), or TalkBack (Android) with per-step keystroke + expected announcement; per-archetype WCAG 2.2 checklists (references/wcag-checklist.md); per-widget keystroke matrices pairing expected NVDA and VoiceOver announcements with the WCAG SC each row verifies (references/widget-matrix.md); and a guided NVDA / VoiceOver session protocol that merges script + checklist into a signed pass/fail session report. Use when authoring an accessibility-acceptance test, checklist, or widget matrix the team will run before sign-off, when scripting a manual a11y audit, OR when walking a tester through a guided screen-reader session.

72

Quality

91%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
screen-reader-test-author
description:
Builds the full manual-accessibility artifact surface: step-by-step screen-reader test scripts for NVDA (Windows), JAWS (Windows), VoiceOver (macOS / iOS), or TalkBack (Android) with per-step keystroke + expected announcement; per-archetype WCAG 2.2 checklists (references/wcag-checklist.md); per-widget keystroke matrices pairing expected NVDA and VoiceOver announcements with the WCAG SC each row verifies (references/widget-matrix.md); and a guided NVDA / VoiceOver session protocol that merges script + checklist into a signed pass/fail session report. Use when authoring an accessibility-acceptance test, checklist, or widget matrix the team will run before sign-off, when scripting a manual a11y audit, OR when walking a tester through a guided screen-reader session.

screen-reader-test-author

Overview

Automated tools (axe-core, pa11y, Lighthouse) catch ~30-40% of accessibility issues - the structural ones. The remaining 60-70% require manual screen-reader testing by an actual human using NVDA / JAWS / VoiceOver / TalkBack. The team needs a repeatable script that any tester (not just the original author) can follow.

This skill takes a user flow and produces that script: per-step keystroke + expected announcement. Two sibling artifact shapes live in references/: a per-archetype WCAG 2.2 checklist generator for spec-review-time acceptance criteria (references/wcag-checklist.md) and per-widget keystroke matrices for hand-run NVDA / VoiceOver passes (references/widget-matrix.md). The "Running the session" section below turns script + checklist into a guided, signed session.

When to use

  • A high-stakes feature ships and needs accessibility sign-off.
  • Automated a11y tools are green but the team wants confidence the actual screen-reader experience is good.
  • Onboarding a new accessibility tester - they need a script to follow until they internalize the conventions.
  • Documenting an "a11y regression repro" when a bug ships.

Step 1 - Pick the screen reader

Most a11y audit conventions cover at least:

Screen readerPlatformBrowser pairing
NVDAWindowsFirefox or Chrome
JAWSWindowsChrome or Edge
VoiceOvermacOS / iOS / iPadOSSafari (macOS); Safari (iOS)
TalkBackAndroidChrome

For the most-coverage-per-effort, test NVDA + Firefox and VoiceOver + Safari - these are also the WAI-recommended pairs.

Start each script by naming the SR + browser + OS combination.

Step 2 - Define the flow

A flow is a sequence of user goals. Don't write step-by-step keystrokes upfront - that's Step 3. The flow is at user-intent level:

flow_name: "User edits their profile email"
preconditions:
  - User is logged in.
  - User is on the /profile page.
  - Screen reader is on; browser virtual cursor is at the page heading.

steps:
  - intent: "Navigate to the profile-edit form via main nav"
  - intent: "Tab through the form to the email field"
  - intent: "Edit the email value to a new valid email"
  - intent: "Submit the form"
  - intent: "Hear the success confirmation announcement"

Step 3 - Per intent, capture keystrokes + expected announcement

For each intent, the script row lists three things:

  • Keystroke - the actual key combo the tester presses.
  • Expected announcement - what the screen reader should say.
  • Why - the WCAG SC or APG pattern the announcement satisfies.

NVDA browse mode navigates with quick-keys (H by heading, F by form field); VoiceOver uses VO (Ctrl+Option) chords:

ActionVoiceOver shortcut
Read next / previousVO + Right / Left arrow
Next headingVO + Cmd + H
Next form controlVO + Cmd + J
Activate (Enter)VO + Space
Web rotorVO + U (form / heading / link list overlay)

A minimal NVDA + Firefox excerpt for the flow "User edits their profile email":

KeystrokeExpected announcement
H"Edit profile, heading level 2" - moves to next heading.
F"Email, edit, blank" - jumps to the first form field.
Tab, Enter"Save changes, button", then a live region announces "Profile saved".

Full per-reader scripts (NVDA + Firefox and VoiceOver + Safari) for this flow, with the WCAG / APG mapping in the "Why" column: references/screen-reader-scripts.md.

VoiceOver announcements are sometimes more verbose than NVDA's (role words like "edit text" where NVDA says "edit"); that is a vendor convention, not a bug to suppress.

Step 4 - Define what counts as PASS

For each step, the announcement should:

  1. Identify the element type - "button", "edit", "link", "heading level 2", etc.
  2. Read the visible label verbatim - if the visible label is "Save changes", the SR should say "Save changes" (not "Submit" or the id attribute).
  3. Convey state - for a checkbox, "checked" / "not checked"; for a button, "expanded" / "collapsed" / "pressed".
  4. Convey position in a set - for a tab, "1 of 4"; for a list item, "1 of 10".

Failures to flag:

FailureLikely cause
Announcement reads CSS class names or id attributesLabel is missing; SR fell back to other text.
Element type is wrong ("link" instead of "button")<a> used as a button; or role="link" on a button.
State is missingaria-expanded / aria-checked not set.
Position-in-set is missingaria-setsize / aria-posinset not set on list items.
Live region announcement doesn't fireRegion added with content already present, OR aria-live="off".

Step 5 - Wire to the test plan

The script becomes a manual test in the project's test plan. Common formats:

Markdown checklist (lightweight)

## A11y Acceptance - User edits profile email

### NVDA + Firefox (Windows)

- [ ] Step 1: Pressing H twice lands on "Profile information" heading.
- [ ] Step 2: Pressing F lands on the email field; announces "Email, edit, blank".
- [ ] Step 3: After submit, a live region announces "Profile saved".

### VoiceOver + Safari (macOS)

- [ ] Step 1: VO+Cmd+H twice lands on "Profile information" heading.
- [ ] Step 2: VO+Cmd+J lands on email; announces "Email, edit text".
- [ ] Step 3: After submit, live region announces "Profile saved".

Sign-off: ___________________ Date: __________

Test-management tool integration

For teams using TestRail / Xray / Zephyr - encode the flow as a manual test case with one step per row, expected result per row. The skill emits the matching format; per test-management-sync (when shipped) the test case can be uploaded automatically.

Running the session

When a component has passed automated scans and static review and a human tester needs a guided manual session to sign off acceptance, compose the script (Steps 1-5) with the component's per-archetype checklist (references/wcag-checklist.md) into one session package. Required inputs: the component's rendered URL, its archetype, and either an existing script or the intent-level flow (Step 2 format). Without a rendered URL the pre-conditions cannot be defined - stop and ask.

Build the session package

Merge script + checklist into a single session document:

  • Header: component name, archetype, URL, date, tester name (blank field), SR + browser pair.
  • NVDA section (Windows): single-letter quick-navigation keys. Per WebAIM NVDA guide: H navigates headings, F form controls, B buttons, K links; Shift+letter moves backwards; NVDA+Space toggles Browse and Focus modes. Per the NVDA user guide, the default NVDA modifier key is Insert (or numpadZero with numLock off).
  • VoiceOver section (macOS): per WebAIM VoiceOver guide, the VO keys are Control+Option; VO+Right/Left reads next/previous item; VO+Cmd+H navigates headings; VO+Cmd+J form controls; VO+Space activates; VO+U opens the Web Rotor.
  • Checklist rows mapped to each script step, so the tester sees the WCAG SC being verified alongside the keystroke.

Walk the tester through it

  1. Pre-conditions block: SR running, browser open to the URL, virtual cursor at page top (NVDA browse mode; VoiceOver with Web Rotor closed).
  2. For each intent step: the keystroke, the expected announcement, the WCAG SC being verified, and a [ ] PASS / [ ] FAIL / [ ] BLOCKED checkbox.
  3. After each overlay or composite step: a focus-return checkpoint per the W3C APG dialog-modal pattern (focus must return to the trigger on close).
  4. After each live-region step: a timing checkpoint (announce within 1-2 s of state change; matches the aria-live="polite" contract per WCAG SC 4.1.3 Status Messages).

Capture the session report

Write the results to a11y-sessions/<component>-<YYYY-MM-DD>-<sr>.md:

## Screen-Reader Session Report

**Component:** <name>  **Archetype:** <archetype>  **URL:** <url>
**Date:** <YYYY-MM-DD>  **Tester:** _______________
**SR + Browser:** NVDA + Firefox | VoiceOver + Safari

| Step | Intent | Keystroke | Expected announcement | WCAG SC | Result |
|------|--------|-----------|-----------------------|---------|--------|
| 1 | Navigate to component heading | H | "<Component>, heading level 2" | 1.3.1 / 2.4.6 | [ ] PASS [ ] FAIL [ ] BLOCKED |

### Summary

| Verdict | Count |
|---------|-------|
| PASS / FAIL / BLOCKED | N |

**Overall:** PASS / FAIL / INCOMPLETE

**Failures to remediate:**
1. <Step N>: <what was announced> vs. <expected> - likely cause + WCAG SC.

**Sign-off:** _______________ Date: __________

The session never modifies component source. iOS VoiceOver and TalkBack (Android) have different gesture models - author a separate mobile script (Step 1) rather than reusing the desktop session.

Anti-patterns

Anti-patternWhy it failsFix
Testing on only one screen readerNVDA bugs ≠ JAWS bugs ≠ VoiceOver bugs.At least two reader/browser pairs (NVDA+Firefox, VoiceOver+Safari).
Asking the developer to test their own workFamiliarity bias; the dev knows how it should sound.Independent tester or accessibility specialist; use the script blindly.
Recording expected announcements verbatim from one versionScreen-reader announcement strings change between versions.Test the announcement contains key elements (label, role, state); avoid string-equality.
Skipping the "Why" columnTester can't generalize from "this announcement was wrong" to "the underlying ARIA pattern is broken."Always link to the WCAG SC or APG pattern.

Limitations

  • Manual. Cannot fully automate; the tester is the QA instrument.
  • Vendor variance. A test script that passes on NVDA might fail on JAWS due to vendor-specific announcement choices; the underlying code may be correct.
  • Mobile tests. TalkBack and iOS VoiceOver have very different gesture sets from desktop SRs; mobile flows need separate scripts.

References

Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/screen-reader-test-author badge