screen-reader-cli
v0.1.0
Published
Screen reader testing CLI — scan pages for accessibility violations with axe-core + Virtual Screen Reader, drive real VoiceOver/NVDA, generate regression tests
Maintainers
Readme
screen-reader-cli
A command-line screen reader testing tool. Scan any page for accessibility violations, generate regression tests, or drive a real screen reader (VoiceOver/NVDA) programmatically.
New here, or not a developer? Start with the friendly guide: elizabeth1979.github.io/screen-reader-cli — what the tool does, who it's for, and how to run your first check in three steps.
Powered by Virtual Screen Reader, Guidepup, axe-core, and Playwright.
What it does
- Scans pages for screen-reader-specific violations (heading skips, missing alt text, missing accessible names, hidden focusable elements, missing landmarks, missing form labels) + axe-core rules
- Drives real screen readers — VoiceOver on Mac, NVDA on Windows — to hear what gets announced
- Generates regression tests — Playwright Test or Vitest files from scan results
- Visual reports — HTML reports with issue tables, heading structure, DOM reading order, and screenshots
- Navigates by element, heading, landmark, link, or form (virtual mode)
- Works offline — everything runs locally, no CDN dependencies
Platform support
| Command | macOS | Windows | Linux |
| ------------------------------------------------ | ----- | ------- | ----- |
| scan, audit, page, nav, speak, repl | ✅ | ✅ | ✅ |
| screenshot, --visual reports | ✅ | ✅ | ✅ |
| live (real VoiceOver/NVDA) | ✅ | ✅ | ❌ |
Everything except live uses a virtual screen reader in headless Chromium and
runs anywhere Node 20+ and Playwright do — including CI. live drives the OS
screen reader, which only exists on macOS (VoiceOver) and Windows (NVDA).
Install
# Requires Node.js 20+
git clone https://github.com/Elizabeth1979/screen-reader-cli.git
cd screen-reader-cli
npm install
npx playwright install chromium
npm linkOnce the package is published to npm this becomes
npm install -g screen-reader-cli(or one-off:npx screen-reader-cli scan <url>), followed bynpx playwright install chromium.
Live mode setup (optional — for real screen reader testing)
To use the live command with a real screen reader, run the one-time setup:
npx @guidepup/setupThis grants the OS permissions needed for screen reader automation:
- macOS: Enables VoiceOver's AppleScript API and adds your terminal to Accessibility permissions
- Windows: Configures NVDA for programmatic control
You only need to do this once per machine. The scan command (virtual mode) works without this step.
Quick start
# Scan a page for accessibility issues (the main command)
screenreader scan https://example.com
# JSON output (for CI pipelines)
screenreader scan https://example.com --json
# Visual HTML report (opens in browser)
screenreader scan https://example.com --visual
# Generate a Playwright regression test file
screenreader scan https://example.com --test
# Generate Vitest stubs instead
screenreader scan https://example.com --test --framework vitest
# Drive the real screen reader on a page
screenreader live read https://example.com
# Interactive REPL
screenreaderCommands
scan — The main command
Scans a page for screen-reader-specific violations using custom DOM checks + axe-core, then outputs a merged, deduplicated report.
screenreader scan <url> # Text report in terminal
screenreader scan <url> --json # JSON (for CI)
screenreader scan <url> --fail-on critical # Exit 1 if critical violations (CI gate)
screenreader scan <url> --visual # HTML report opens in browser
screenreader scan <url> --test # Generate Playwright test file
screenreader scan <url> --test --framework vitest # Generate Vitest stubs
screenreader scan <url> --test --output my-tests.test.js # Custom output path
screenreader scan <url> --ai # AI analysis of results
screenreader scan <url> --ai --model sonnet # Use Claude Sonnet
screenreader scan <url> --ai --model gpt-4o # Use GPT-4o
screenreader scan <url> --ai --provider ollama # Use local model (free)AI analysis (--ai)
Get plain-language explanations, prioritized fix suggestions with code examples, and a screen reader experience score. Supports multiple providers:
| Provider | Flag | Models | API Key |
| --------- | ---------------------- | -------------------------------------------- | -------------------------------------- |
| Gemini | --provider gemini | flash (default), pro | GEMINI_API_KEY (free tier available) |
| Anthropic | --provider anthropic | haiku, sonnet (default), opus | ANTHROPIC_API_KEY |
| OpenAI | --provider openai | gpt-4o-mini (default), gpt-4o, o3-mini | OPENAI_API_KEY |
| Ollama | --provider ollama | Any installed model | None (free, local) |
Auto-detects provider from model name: --model sonnet → Anthropic, --model gpt-4o → OpenAI, --model flash → Gemini. Unknown models default to Ollama. If no provider or model is specified, picks the first available API key (priority: Gemini → Anthropic → OpenAI → Ollama).
What it checks:
- Heading hierarchy (no skips, e.g. h1 → h3)
- Missing alt text on images
- Missing accessible names on buttons and links (including icon-only buttons/links)
- Missing form labels
- Missing main landmark
- Focusable elements inside
aria-hidden="true" - All axe-core WCAG 2 AA rules
Works with URLs and local files:
screenreader scan test/fixtures/violations.htmlCI usage (--fail-on)
--fail-on <severity> makes scan exit with code 1 when violations at or
above that severity are found, so it can gate a pipeline:
--fail-on critical— fail only on critical violations--fail-on moderate— fail on critical or moderate--fail-on minor— fail on any violation
GitHub Actions example:
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install -g screen-reader-cli
- run: npx playwright install --with-deps chromium
- name: Accessibility gate
run: screenreader scan https://staging.example.com --fail-on criticalCombine with --json to also archive the full results as a build artifact.
dashboard — Point-and-click scanning
Prefer buttons over commands? Start the dashboard once and run every scan from your browser:
sr # shortcut — opens the dashboard
screenreader dashboard # same thing, full command
screenreader dashboard --port 5000 # pick another portsr is a short alias for the whole CLI: on its own it opens the dashboard,
and sr scan <url> works exactly like screenreader scan <url>.
Type a page address, press Scan, and the results appear with a link to the full visual report. A history of past scans is kept while the dashboard is running. Everything stays on your machine — the dashboard only listens on localhost.
live — Real screen reader testing
Drives VoiceOver (macOS) or NVDA (Windows) on a real page. Auto-detects your OS, or override with --reader.
# Read the full page — logs every announcement
screenreader live read <url>
screenreader live read <url> --json
screenreader live read <url> --steps 50 # Limit traversal steps
# Test mode — traverses and detects issues (empty announcements, focus traps)
screenreader live test <url>
screenreader live test <url> --json
# Interactive — opens browser + screen reader, keeps it running
screenreader live open <url>
# Force a specific reader
screenreader live read <url> --reader nvda
screenreader live read <url> --reader voiceoverRequires one-time setup: npx @guidepup/setup (see Install section above).
page — Page navigation
screenreader page open <url> # Open a URL or local HTML file
screenreader page info # Show current page title and URLnav — Navigate elements
screenreader nav next # Move to next element
screenreader nav previous # Move to previous element
screenreader nav heading # Jump to next heading
screenreader nav landmark # Jump to next landmark
screenreader nav link # Jump to next link
screenreader nav form # Jump to next formAll nav commands accept --url <url> to open a page first and --json for structured output.
speak — Query spoken phrases
screenreader speak last # Last spoken phrase
screenreader speak log --steps 10 # Navigate 10 steps, show log
screenreader speak find "Sign up" --url https://example.comaudit — Full page traversal
screenreader audit <url> # Traverse entire page
screenreader audit <url> --summary # Include heading/landmark summary
screenreader audit <url> --json # JSON output
screenreader audit <url> --max 1000 # Increase element limit (default: 500)screenshot — Capture elements or pages
screenreader screenshot --url <url> --full --output page.png
screenreader screenshot --url <url> --navigate 5 --output element.pngrepl — Interactive mode
screenreader # Enters REPL (default when no command given)
screenreader repl # Same thing, explicitIn the REPL, navigation state persists across commands. You can also start a real screen reader session:
screenreader> page open https://example.com
Opened: Example Domain (https://example.com/)
screenreader> nav heading
heading, Example Domain, level 1
screenreader> nav next
More information...
screenreader> screenshot element.png
Screenshot saved: element.png
screenreader> live start
VoiceOver started. Use "live next", "live previous", "live log", etc.
screenreader> live next
Example Domain, heading level 1
screenreader> live log
1. Example Domain, heading level 1
screenreader> live stop
VoiceOver stopped.
screenreader> quitJSON output
All commands support --json for machine-readable output:
screenreader audit https://example.com --summary --json{
"phrases": ["document", "heading, Example Domain, level 1", "..."],
"total": 12,
"headings": ["heading, Example Domain, level 1"],
"landmarks": ["main"],
"links": ["link, More information..."],
"summary": {
"totalElements": 12,
"headingCount": 1,
"landmarkCount": 1,
"linkCount": 1
}
}How it works
Virtual mode (scan, page, nav, audit, etc.)
- Playwright launches a headless Chromium browser
- Virtual Screen Reader is injected into the page context for DOM traversal
- Custom violation checks run against the DOM (heading hierarchy, accessible names, etc.)
- axe-core is injected for comprehensive WCAG 2 AA rule coverage
- Results are merged and deduplicated
The Virtual Screen Reader implements the same W3C accessibility specifications that real screen readers follow — ACCNAME, CORE-AAM, HTML-AAM, WAI-ARIA 1.2, and more.
Live mode (live)
- Playwright launches a visible browser (screen readers need a real window)
- Guidepup starts VoiceOver (macOS) or NVDA (Windows)
- The screen reader traverses the page — you hear what it actually announces
- Results are captured via guidepup's API (
lastSpokenPhrase(),spokenPhraseLog())
Security notes
--chrome-profilereuses your real Chrome profile — cookies and logged-in sessions included. Only use it against URLs you trust (your own staging environments); scanning an untrusted site with it exposes your authenticated sessions to that site.- Virtual mode disables the target page's Content-Security-Policy
(
bypassCSP) so the Virtual Screen Reader bundle can be injected. This is required for the tool to work on CSP-strict sites, but it means the page runs with weaker protections during the scan — again, point it at pages you trust. - AI analysis (
--ai) sends scan results (violation messages, selectors, page title/URL) to the provider you select. Use--provider ollamato keep everything local.
Architecture
flowchart LR
CLI["bin/cli.js\n(commander)"]
CLI --> SCAN["scan"]
CLI --> VIRT["page · nav · speak\naudit · repl"]
CLI --> LIVE["live"]
SCAN --> PW["Playwright\nheadless Chromium"]
PW --> AXE["axe-core\nWCAG 2 AA rules"]
PW --> STRUCT["headings · landmarks\nDOM reading order"]
AXE --> MERGE["merge + dedupe\n(scanner.js)"]
STRUCT --> MERGE
MERGE --> TEXT["text report"]
MERGE --> JSON["--json"]
MERGE --> HTML["--visual HTML report"]
MERGE --> TESTS["--test generated tests"]
MERGE --> AI["--ai analysis\nGemini · Claude · GPT · Ollama"]
VIRT --> BRIDGE["bridge.js\nVirtual Screen Reader"]
BRIDGE --> PW2["Playwright\nheadless Chromium"]
LIVE --> GP["Guidepup"]
GP --> VO["VoiceOver (macOS)"]
GP --> NVDA["NVDA (Windows)"]Three engines, one CLI:
- Scan — Playwright loads the page, axe-core finds violations, custom code extracts structure, and everything merges into one report (text, JSON, HTML, generated tests, or AI analysis).
- Virtual — the Virtual Screen Reader is injected into the page so
nav/audit/speakcan traverse it the way a screen reader would. - Live — Guidepup drives the real OS screen reader so you hear actual announcements.
Roadmap
Recently shipped:
- [x]
--fail-on <severity>exit-code gate for CI pipelines - [x] Cross-platform support for
--visualreports (macOS/Windows/Linux) - [x] Real assertions in
--testgenerated files - [x] Local dashboard (
screenreader dashboard) — scan from the browser, no terminal after launch
Planned (roughly in order):
- [ ] Element screenshots — capture an image of each failing element and embed it in the visual report
- [ ] Flow capture — screenshot each step of a multi-page/multi-step flow as it's scanned
- [ ] Violation context — include the DOM path and accessibility-tree node for each violation in reports (today: selector + HTML snippet)
- [ ] Richer AI fix suggestions — per-violation code-level fixes with the
element's full context (today:
--aigives prioritized fixes + a score) - [ ] Asset capture — download page images during a scan for audit evidence
- [ ] Multi-page crawling — scan a whole site from a sitemap or crawl
- [ ] Baseline & diff — fail CI only on new violations
- [ ] GitHub Action — a published action wrapping
scan --fail-on - [ ] Screen reader transcript diff — compare what's announced before vs. after a change
Suggestions welcome — open an issue.
Use cases
- Accessibility testing — audit any site's screen reader experience from CI/CD
- AI agents — give agents structured, semantic understanding of web pages
- Developer workflows — quickly check heading hierarchy, landmark structure, ARIA usage
- Automated QA — validate accessibility in build pipelines with JSON output
Claude Code companion skill
If you use Claude Code, this repo ships a companion skill + slash command that lets Claude run audits on demand:
/sr-audit https://example.comClaude runs scan + audit paired, saves text + JSON to disk, and surfaces the top issues in chat. See skills/claude-code/README.md for install instructions and what it does.
Testing
# Run all local tests (headless Chromium, no network needed)
npm test
# E2E tests (requires network — scans https://example.com)
npm run test:e2e
# Individual suites
node --test test/scan.test.js # Scan command
node --test test/live.test.js # Live command
node --test test/commands.test.js # Virtual mode commands
node --test test/bridge.test.js # VSR bridge
node --test test/audit.test.js # Audit command
node --test test/daemon.test.js # Browser lifecycleContributions welcome — see CONTRIBUTING.md.
Built with
- axe-core — Accessibility rule engine (WCAG 2 AA)
- @guidepup/virtual-screen-reader — Screen reader simulation engine
- @guidepup/guidepup — Real screen reader driver (VoiceOver + NVDA)
- Playwright — Browser automation
- Commander.js — CLI framework
License
MIT
