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
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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.
Most a11y audit conventions cover at least:
| Screen reader | Platform | Browser pairing |
|---|---|---|
| NVDA | Windows | Firefox or Chrome |
| JAWS | Windows | Chrome or Edge |
| VoiceOver | macOS / iOS / iPadOS | Safari (macOS); Safari (iOS) |
| TalkBack | Android | Chrome |
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.
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"For each intent, the script row lists three things:
NVDA browse mode navigates with quick-keys (H by heading, F by
form field); VoiceOver uses VO (Ctrl+Option) chords:
| Action | VoiceOver shortcut |
|---|---|
| Read next / previous | VO + Right / Left arrow |
| Next heading | VO + Cmd + H |
| Next form control | VO + Cmd + J |
| Activate (Enter) | VO + Space |
| Web rotor | VO + U (form / heading / link list overlay) |
A minimal NVDA + Firefox excerpt for the flow "User edits their profile email":
| Keystroke | Expected 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.
For each step, the announcement should:
id attribute).Failures to flag:
| Failure | Likely cause |
|---|---|
Announcement reads CSS class names or id attributes | Label 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 missing | aria-expanded / aria-checked not set. |
| Position-in-set is missing | aria-setsize / aria-posinset not set on list items. |
| Live region announcement doesn't fire | Region added with content already present, OR aria-live="off". |
The script becomes a manual test in the project's test plan. Common formats:
## 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: __________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.
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.
Merge script + checklist into a single session document:
[ ] PASS / [ ] FAIL / [ ] BLOCKED
checkbox.aria-live="polite" contract per
WCAG SC 4.1.3 Status Messages).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-pattern | Why it fails | Fix |
|---|---|---|
| Testing on only one screen reader | NVDA bugs ≠ JAWS bugs ≠ VoiceOver bugs. | At least two reader/browser pairs (NVDA+Firefox, VoiceOver+Safari). |
| Asking the developer to test their own work | Familiarity bias; the dev knows how it should sound. | Independent tester or accessibility specialist; use the script blindly. |
| Recording expected announcements verbatim from one version | Screen-reader announcement strings change between versions. | Test the announcement contains key elements (label, role, state); avoid string-equality. |
| Skipping the "Why" column | Tester can't generalize from "this announcement was wrong" to "the underlying ARIA pattern is broken." | Always link to the WCAG SC or APG pattern. |
wcag-keyboard-navigation,
aria-authoring-patterns - patterns this skill references for the "Why" column.