Testing with NVDA & VoiceOver in a Dev Workflow
Automated tools find missing attributes. They cannot tell you that a form reads as "edit, blank, edit, blank" or that your carefully written status message arrives four seconds after the user has moved on. Those findings require listening — and the reason most teams never do is not hostility, it is that nobody showed them the eight commands that make a screen reader usable for testing.
This guide is that introduction: what to install, the commands to learn first, a repeatable ten-minute script, and how to write up what you hear. It is the practical companion to Screen Reader Compatibility Testing.
WCAG Success Criteria Addressed (verified by listening):
1.3.1 Info and Relationships2.4.3 Focus Order3.3.2 Labels or Instructions4.1.2 Name, Role, Value4.1.3 Status Messages
Prerequisites
- Windows with NVDA installed, or macOS with VoiceOver (already present —
Cmd+F5toggles it). - Headphones, which make this dramatically less disruptive in a shared office.
- A page you built recently, so the mismatch between what you intended and what you hear is informative.
Learn Eight Commands, Not Eighty
Screen readers have hundreds of shortcuts. Testing needs a handful, and the rest can wait until you need them.
Implementation Guidelines:
- Learn the modifier first: NVDA uses
Insert(orCapsLockin laptop mode); VoiceOver usesCtrl+Option, writtenVO. - Practise stopping speech before anything else —
Ctrlin NVDA,Ctrlin VoiceOver. Being unable to stop the torrent is what makes first attempts miserable. - Use browse mode (NVDA's default on a page) for reading and forms mode for typing; NVDA switches automatically, and
Insert+Spacetoggles it manually. - On macOS, turn on "VoiceOver can be controlled with the keyboard" and learn the rotor (
VO+U) before anything else — it is where headings, landmarks and links live.
NVDA VoiceOver (VO = Ctrl+Option)
Ctrl stop speech Ctrl stop speech
Down arrow read next VO+Right read next
H / D / K heading / VO+U rotor: headings,
landmark / landmarks, links
link
Tab next control Tab next control
Insert+F7 elements list VO+U then arrows element lists
Insert+Down read all VO+A read all
A Ten-Minute Script You Can Repeat
An unstructured listen produces vague impressions. A fixed script produces findings you can act on, and it is short enough to run before every pull request that touches UI.
Implementation Guidelines:
- Run the five steps in order; each one depends on the previous being sane.
- Write down the exact words you hear, not your interpretation. "Announces 'button'" is a finding; "the button is broken" is not.
- Test one page per session at first. Ten focused minutes beat an hour of frustrated wandering.
- Repeat the same script after fixing, so you can confirm the change rather than assuming it.
Know What Each Reader Gets Right
NVDA and VoiceOver disagree, and the disagreement is informative: NVDA is stricter about ARIA, VoiceOver is more forgiving and more idiosyncratic about ordering.
Implementation Guidelines:
- Treat NVDA's behaviour as the stricter standard: if something works there it usually works elsewhere, and if it does not, the markup is genuinely ambiguous.
- Do not "fix" for one reader with a hack that breaks the other. If two readers disagree, the underlying semantics are usually wrong.
- Remember browser pairing: NVDA with Firefox or Chrome, VoiceOver with Safari. Cross-pairings exist but produce results real users rarely see.
- Check mobile separately. iOS VoiceOver has no Tab key, so a control reachable only by tabbing is unreachable there.
Report What You Heard, Not What You Assume
A useful bug report from a listening session contains the transcript. It is what turns "the modal is not accessible" into a fix someone can make.
Implementation Guidelines:
- Quote the announcement verbatim, including the role and state words.
- State what you expected to hear, so the gap is explicit.
- Include the reader, browser and OS versions — behaviour changes between releases.
- Attach the sequence of keys you pressed, so the finding is reproducible by someone who has never used the reader.
Reader: NVDA 2026.1 + Firefox 141 (Windows 11)
Steps: Tab to "Filters", press Enter, Tab twice
Heard: "button" … "button" … "checkbox not checked"
Expect: "Filters, button, collapsed" … "Framework, React, checkbox not checked"
Issue: The disclosure has no accessible name or state, and the checkbox
group is not wrapped in a fieldset, so its legend is never announced.
How to Verify
- Fix and re-listen. Run the same five steps after the change; the transcript should now match the expectation you wrote down.
- Cross-reader check. Confirm the fix in both NVDA and VoiceOver where the finding involved ARIA — agreement across readers is a good sign the semantics are right.
- Automated backstop. Add a jest-axe or Playwright assertion for anything mechanical you found, so it cannot regress — see Component Testing with jest-axe.
- Keyboard-only pass. Repeat with the screen reader off; some findings are focus bugs wearing an announcement costume.
- Mobile pass. For anything touch-related, repeat on iOS VoiceOver, where the gesture model exposes different problems.
Common Accessibility Mistakes
- Testing only with the screen reader you find easiest. VoiceOver's tolerance hides markup problems NVDA would report immediately.
- Fixing until the reader says something. The goal is a sensible announcement, not any announcement — "button button" is speech, not success.
- Treating a screen reader as an assistive-technology simulator. It reflects one user's configuration; real users customise verbosity, speed and rotor heavily.
- Skipping the "read all" step. It is the fastest way to find content that is duplicated, skipped or in the wrong order.
- Reporting impressions instead of transcripts. Nobody can fix "it sounds confusing".
- Testing once, at the end. The findings arrive too late to influence the design, which is where most of them originate.
Conclusion
Fifteen minutes with a screen reader tells you things no scanner can: whether the reading order matches the design, whether your announcements arrive at a useful moment, whether a control's name means anything out of context. Learn eight commands, run the same five-step script every time, quote what you hear, and start with NVDA if you have a choice. It is not a substitute for testing with actual screen reader users — nothing is — but it is the difference between shipping a page you have listened to and shipping one you have only looked at.
One habit makes this stick better than any tooling: run the five-step script on your own work before anyone else sees it. Findings discovered by the person who wrote the component get fixed in minutes, while the same finding routed through a ticket costs a context switch on both sides and often arrives after the design has hardened. Fifteen minutes at the end of a feature, on the branch, is the cheapest accessibility process most teams will ever adopt — and it is the one that changes how the next component gets written.
Frequently Asked Questions
Do I need to be good at using a screen reader? No, and it is worth being explicit that you never will be as fluent as a daily user. That is fine: the goal is to hear what your page produces, not to simulate expertise. Most findings surface in the first two minutes of any competent listening pass.
Is JAWS worth testing with too? It has substantial enterprise use and it is commercial. If your audience is enterprise, add it; otherwise NVDA covers a similar Windows behaviour space for free. The forty-minute JAWS demo mode is enough for occasional verification.
Can browser extensions replace a real screen reader? Accessibility tree inspectors and simulators are useful for debugging names and roles, and they do not tell you what a page sounds like — the pacing, the interruptions, the repetition. Use them alongside a real reader, not instead of one.
How do I test in CI? You cannot automate what a screen reader announces in any practical way. What you can automate is the underlying data: assert accessible names with Testing Library's role queries, assert live-region text, assert focus after transitions. Those cover the mechanical half — see testing ARIA live regions with Jest and Testing Library.
What if I have no Windows machine? Start with VoiceOver on macOS and iOS; it is already installed. Add NVDA in a Windows virtual machine when you can, since it is stricter and represents a larger share of desktop use — but a VoiceOver-only habit is far better than none.
Related guides
- Screen Reader Compatibility Testing — the parent guide on differences between readers.
- Testing ARIA Live Regions with Jest and Testing Library — automating the announcements you verified by ear.
- Accessible Navigation & Landmark Structure — the landmark and heading passes in step 2 and 3.
- Gating Accessibility in CI/CD Pipelines — where the automatable findings should end up.