How to test web accessibility with NVDA

NVDA (NonVisual Desktop Access) is a free, open-source screen reader for Windows maintained by NV Access. It is the de facto baseline for web accessibility testing: it is free, has the largest user share among screen-reader users globally (WebAIM Screen Reader Survey), and reproduces most issues that VoiceOver and JAWS users encounter.

This how-to walks through installing NVDA, learning the minimum useful keys, and running a smoke test against an arbitrary page.

Install — portable mode

A portable NVDA install does not require admin rights on your machine, which matters if you are testing on a managed laptop:

  1. Download from nvaccess.org/download.
  2. Run the installer and choose “Create Portable Copy”.
  3. Place the portable folder somewhere persistent.

Run the portable executable. NVDA starts speaking immediately. To stop speech: Ctrl. To enable Speech Viewer (a window that shows what NVDA just said — invaluable for visual debugging): NVDA menu → Tools → Speech Viewer.

The keys you actually need

NVDA has a key called the NVDA modifier — defaults to Insert or CapsLock (configurable). On laptops, set it to CapsLock in Preferences → Keyboard → “Use CapsLock as NVDA modifier key”.

ActionKeys
Stop speakingCtrl
Pause / resumeShift
Read all (continuous)NVDA + ↓
Read previous / next line /
Read previous / next character /
Next heading / list / tableH / L / T
Next linkK
Next form controlF
Next landmarkD
Elements list (everything)NVDA + F7
Toggle browse / focus modeNVDA + Space
Read accessible name + stateNVDA + Tab

Browse mode is the default for static content; focus mode kicks in automatically on form controls. Toggling is needed mostly when a custom widget claims to be a textarea but is not.

A 10-minute smoke procedure

For each page you ship a meaningful change to, run this:

  1. Open the page in Firefox. Firefox has the most predictable NVDA pairing; Chrome works but occasionally misroutes ARIA live regions.
  2. Read top-to-bottom. NVDA + ↓. Listen to the first 60 seconds. Stop on any utterance that lands as “button” or “link” with no further context, or that announces a visible heading as plain text.
  3. Pull the elements list. NVDA + F7. Switch tab to Headings. The hierarchy should make sense: an article should have one h1, then h2 siblings, then h3 children. A flat list of nineteen h2s usually means someone styled paragraphs as headings.
  4. Switch to Landmarks. Should include banner, navigation, main, contentinfo at minimum. Missing main is a common bug.
  5. Tab through interactive elements. Each Tab stop should announce name + role + state. “Email address, edit, required, blank” is correct. “Edit, blank” is missing the name.
  6. Activate one form. Submit with errors. The error messages should be announced via a live region or by moving focus to the first error. If silence follows, you have a 4.1.3 failure.
  7. Open one dialog. Focus should move into the dialog; Escape should close it; focus should return to the trigger. NVDA should announce the dialog name and role on open.

If all seven steps pass, the page is in good shape for a v0.1 release on the screen-reader axis. If two or more fail, do not ship; fix.

Speech Viewer for documentation

When you find an issue, capture the announcement string from Speech Viewer and paste it into the bug report. “NVDA reads: ‘edit, blank’” is a complete, reproducible bug; “screen reader is broken” is not.

Browser pairings

  • Firefox + NVDA — baseline. Strongest ARIA support, fewest surprises.
  • Chrome + NVDA — works, with occasional live-region quirks.
  • Edge + NVDA — equivalent to Chrome (Chromium engine).

For VoiceOver coverage on macOS/iOS, the canonical reference is Apple’s VoiceOver User Guide. For JAWS, the enterprise QA combination, key chords differ but the smoke procedure is portable.

When this is hard

  • Custom comboboxes. Browse mode and focus mode disagree about who owns arrow keys. The combobox should explicitly switch NVDA to focus mode (role="combobox" plus aria-expanded); if it does not, users cannot navigate options.
  • Dynamic page transitions. SPA route changes are silent unless you manually announce them. Move focus to the new <h1> on navigation, or use aria-live="polite" on a dedicated announcement container.
  • Heading-only navigation. Some users navigate exclusively by H. If your headings do not summarise the section, those users get no table-of-contents.

The single most useful habit: keep NVDA installed and run the 10-minute smoke before every release. The aggregate cost is small; the regression catch rate is large.