Catch accessibility issues while you code, not after release.
a11y-shiftleft-cli is a local-first, shift-left accessibility audit tool for
frontend developers who are not accessibility specialists. Run one command
against a local, staging, or preview URL. It opens your app in a browser,
safely explores UI states, deduplicates overlapping WCAG-oriented findings, and
generates a visual HTML report with screenshots, keyboard evidence, grouped
issues, and practical fix guidance.
Use it locally during development or add it to CI/CD so pull requests get repeatable accessibility feedback before issues reach production.
It works with any rendered web app or website, including React, Vue, Angular, Next.js, Svelte, Astro, Rails, Django, and static HTML. Optional source-code adapters add framework-aware checks for React, Vue, and Angular. For SPAs and dynamic pages, the browser audit checks the rendered UI after client-side data loads, not just the initial HTML source.
- Replaces several separate review steps with one developer-friendly workflow.
- Turns raw rule output into grouped, prioritized findings.
- Helps developers understand what to fix without becoming accessibility specialists first.
- Lets teams start report-only, then tighten CI gates after they understand the findings.
The CLI orchestrates established engines instead of replacing their rule systems:
| Engine | Used for |
|---|---|
@axe-core/playwright |
Automated accessibility rules against the rendered page |
| Playwright | Browser automation, safe UI-state exploration, screenshots, and keyboard evidence |
| ESLint adapters | Optional source checks for React, Vue, and Angular projects |
| Lighthouse | Optional accessibility score comparison when installed |
The report combines these signals with deduplication, WCAG labels, visual evidence, and manual-review gaps. It is an evidence workflow, not a replacement for the underlying tools or a conformance certification.
Use this when your app already runs locally. You need Node.js 18 or newer, but you do not need to configure a framework first.
Privacy first: the CLI runs in your project environment and does not upload source code, screenshots, URLs, cookies, auth state, or report data to an external analysis server. Reports stay local by default.
Screenshots mask common sensitive fields such as passwords, emails, phone
numbers, payment inputs, and elements marked with data-a11y-sensitive. Use
--no-screenshots for private, authenticated, or production customer pages.
- Install the CLI and the Chromium browser used by Playwright:
npm install --save-dev a11y-shiftleft-cli
npx playwright install chromium- Start your app in another terminal:
npm run dev- Run your first visual audit. Replace
YOUR_PORTwith the port printed by your dev server:
export APP_URL=http://localhost:YOUR_PORT
npx a11y-shiftleft-cli audit --url $APP_URL --out reports --open- If the report does not open automatically:
open reports/a11y-report.htmlOn Linux use xdg-open reports/a11y-report.html. On Windows PowerShell use
start reports/a11y-report.html.
Expected result: reports/a11y-report.html opens with summary metrics,
screenshots, grouped findings, WCAG labels, and fix guidance.
If your app requires login, create a local Playwright auth state first. The CLI opens a real browser; you log in manually, including 2FA if needed, and then the session is saved locally.
Enter your username, password, and 2FA code in the browser window opened by the command, not in the terminal. The CLI does not ask for or store your password.
Authenticated scans are still local-first: login cookies, storage state, screenshots, URLs, and reports are not sent to an external server by the CLI.
npx a11y-shiftleft-cli auth login --url https://example.com/login
npx a11y-shiftleft-cli audit --url https://example.com/account --auth-state .a11y-auth/state.json --out reports --openThe generated .a11y-auth/ folder is added to .gitignore by default. Do not
commit auth-state files because they may contain session cookies.
See the authenticated pages recipe for
post-login redirects, existing Playwright storageState files, keyboard checks,
and privacy settings.
If a public site shows a CAPTCHA or "verify you are human" page, run a visual audit in manual verification mode. The CLI opens a visible browser, waits while you complete the challenge yourself, and then continues the scan:
npx a11y-shiftleft-cli audit --url $APP_URL --out reports --pause-on-human-verification --openAfter the first local audit works, create npm scripts, the starter config,
report .gitignore entries, and a report-only CI workflow:
npx a11y-shiftleft-cli setup --url $APP_URL --start-command "npm run dev"GitHub Actions is the default. For GitLab CI, add --ci gitlab:
npx a11y-shiftleft-cli setup --ci gitlab --url $APP_URL --start-command "npm run dev"For CircleCI, add --ci circleci:
npx a11y-shiftleft-cli setup --ci circleci --url $APP_URL --start-command "npm run dev"For Jenkins or another shell-based runner, generate a portable script:
npx a11y-shiftleft-cli setup --ci shell --url $APP_URL --start-command "npm run dev"This creates .a11y-shiftleft.json, adds a11y:audit and a11y:check npm
scripts when package.json exists, updates .gitignore, and adds a CI workflow
that installs the project, starts your app, runs accessibility checks, and keeps
reports as CI artifacts. Shell setup creates scripts/a11y-ci.sh. GitHub
workflows also post a pull request comment. The default quality gate is
report-only, so teams can adopt it before failing builds on legacy issues.
After setup, local checks become:
npm run a11y:audit
npm run a11y:checkCopy-paste CI examples are available for GitHub Actions, GitLab CI, and CI/CD without SaaS.
For an existing pipeline, the smallest integration is one npm script:
{
"scripts": {
"test:a11y": "a11y-shiftleft-cli check --dynamic --url $APP_URL --out reports"
}
}You do not need an adapter for the visual browser audit. audit and dynamic
check run against any rendered URL.
Adapters add source-code checks on top of the browser audit. Install only the adapter for the framework your project uses:
| Project | Optional adapter | What it adds |
|---|---|---|
| React / Next.js | @a11y-shiftleft/react |
JSX/TSX accessibility lint rules |
| Vue | @a11y-shiftleft/vue |
Vue template accessibility lint support |
| Angular | @a11y-shiftleft/angular |
Angular template accessibility lint support |
npm install --save-dev @a11y-shiftleft/react
npm install --save-dev @a11y-shiftleft/vue
npm install --save-dev @a11y-shiftleft/angularIf you are not sure, skip adapters first and run the browser audit. Add an adapter later when you want static source findings in the same report.
- A local visual HTML report you can open in your browser.
- Annotated screenshots that show where issues were found.
- WCAG A/AA labels, severity, confidence, and user-impact hints.
- Fix guidance, including contrast ratios and color suggestions.
- Separate
needs reviewfindings when axe cannot prove a result automatically, such as text over images, gradients, video, or complex overlays. - Keyboard evidence and manual-review tasks for things automation cannot prove.
This is the main output of audit:
Start with audit when a person needs to review the result. Use check when a
pipeline needs a fast pass/fail signal.
| Command | Use it when | Main output | Best for | Typical runtime |
|---|---|---|---|---|
audit |
You want a deeper local review with visual evidence | Visual HTML report with screenshots, explored states, keyboard evidence, manual-review checklist, JSON, and Markdown | Local debugging, design/dev review, sharing evidence with a team | Slower |
check |
You want a quick automated check against known URL(s) | JSON and Markdown reports, optional CI summary and baseline comparison | Pull requests, CI gates, npm scripts, regression checks | Faster |
explore |
You want to debug UI-state discovery itself | Visual exploration report for safe clicks, links, dialogs, and screenshots | Tuning depth, safe-mode blocks, screenshots, and state discovery | Medium |
Rule of thumb: run audit --url $APP_URL --out reports --open first. After the
main issues are understood, add check --dynamic --url $APP_URL --out reports
to CI.
The commands below assume APP_URL is set to your local, staging, or preview URL.
| Command type | Need | Command |
|---|---|---|
audit |
First local review | npx a11y-shiftleft-cli audit --url $APP_URL --out reports --open |
audit |
Quick risk triage | npx a11y-shiftleft-cli audit --url $APP_URL --profile risk --out reports |
audit |
Broader local scan | npx a11y-shiftleft-cli audit --url $APP_URL --max-depth 3 --limit 50 --out reports |
audit |
Check one component or page area | npx a11y-shiftleft-cli audit --url $APP_URL --scope '#main' --out reports |
check |
Fast CI or PR check | npx a11y-shiftleft-cli check --dynamic --url $APP_URL --out reports |
explore |
Debug visual state discovery | npx a11y-shiftleft-cli explore --url $APP_URL --out reports |
setup |
Create npm scripts, config, .gitignore, and CI workflow |
npx a11y-shiftleft-cli setup --url $APP_URL --start-command "npm run dev" |
generate-ci |
Regenerate only CI workflow files | npx a11y-shiftleft-cli generate-ci --provider github --url $APP_URL --start-command "npm run dev" |
pr-comment |
Post an existing local report to a GitHub PR | npx a11y-shiftleft-cli pr-comment --repo owner/repo --pr 123 --report reports --include-labels |
ticket export |
Create reviewable Jira/Linear/GitHub issue drafts from a report | npx a11y-shiftleft-cli ticket export --report reports/a11y-report.json --out reports/a11y-tickets.md |
evidence export |
Export JSON/JSONL finding evidence for scripts or validation studies | npx a11y-shiftleft-cli evidence export --report reports/a11y-report.json --out reports/a11y-evidence.jsonl --format jsonl |
ignore audit |
Review expired or invalid temporary exceptions | npx a11y-shiftleft-cli ignore audit |
ignore cleanup-plan |
Generate a read-only cleanup plan for stale exceptions | npx a11y-shiftleft-cli ignore cleanup-plan |
doctor |
Diagnose setup problems | npx a11y-shiftleft-cli doctor --url $APP_URL |
By default, audit explores up to 2 interaction levels from the start page.
--max-depth lets you change that safety limit; it does not mean "scan forever"
or "visit every possible page."
The audit automatically explores safe links, buttons, dialogs, forms, theme states, and same-origin UI transitions within bounded depth and state limits. It is designed to find issues earlier, not to certify that every page and every WCAG criterion has been fully tested.
Use 1 for a quick smoke test, the default 2 for most local reviews, and 3
or more only when you intentionally want a broader scan.
Use --scope <selector> when you want browser checks and safe UI-state
exploration to stay inside one component, dialog, checkout step, or page
section.
Use --wait-ms <ms>, --wait-for-selector <selector>,
--wait-until-url <pattern>, or --wait-until-path <path> when a SPA loads
data, redirects after login, or renders authenticated content after the first
paint:
npx a11y-shiftleft-cli audit --url $APP_URL --wait-for-selector "[data-page-ready]" --out reports
npx a11y-shiftleft-cli audit --url $APP_URL --wait-until-path /dashboard --out reportsUse --hide-elements <selectors> when cookie banners, sticky ads, chat widgets,
or other non-product overlays make screenshots noisy. Hidden selectors are
recorded in the visual and Markdown reports.
Use --browser chromium|firefox|webkit when you need evidence from another
browser engine. Use --mobile for the default phone profile, --tablet for the
default tablet profile, or --device "<Playwright device>" when you need an
exact Playwright preset. Install that browser first, for example:
npx playwright install webkitAudit profiles are shortcuts:
risk: faster triage with lower depth and fewer explored states.validation: the standard local evidence profile.full: broader scan with keyboard activation checks and Lighthouse comparison. Installlighthousefirst when you want this comparison:
npm install --save-dev lighthouseExplicit flags override profile defaults, for example:
npx a11y-shiftleft-cli audit --url $APP_URL --profile risk --max-depth 2 --out reportsAfter the report opens:
- Start with the "Fix First" and screenshot sections.
- Check the manual-review tasks for keyboard, screen reader, content, and forms.
- Re-run the same command after fixing issues.
Reports and screenshots usually should not be committed. Run init --gitignore
once to add common report paths. For private pages, add --no-screenshots.
More recipes for privacy and local artifacts, user impact review, external validation, quality gates in existing projects, browser profiles, hidden overlays, and advanced configuration are in Configuration and Recipes.
Use --standard when the report needs a specific evidence context:
npx a11y-shiftleft-cli audit --url $APP_URL --standard wcag22-aa --out reports
npx a11y-shiftleft-cli audit --url $APP_URL --standard section508 --out reports
npx a11y-shiftleft-cli audit --url $APP_URL --standard ada-title-ii --out reports
npx a11y-shiftleft-cli audit --url $APP_URL --standard en301549 --out reportsAvailable presets: wcag22-aa, section508, ada-title-ii, and en301549.
They adjust labels, evidence guidance, and report context; they do not certify
legal compliance. See the Section 508,
ADA Title II, and
EN 301 549 recipes for more context.
- The report supports accessibility review; it is not a WCAG, ADA, Section 508, EN 301 549, or EAA certification.
- Use automated evidence together with manual keyboard, screen-reader, content, and task-flow review.
- Some public websites block automated scans with bot detection or CAPTCHA.
- Third-party embeds such as YouTube, Vimeo, Spotify, Google Maps, and CodePen are marked separately when ownership can be detected.
Run the local demo
This repository includes a React/Vite demo with intentional accessibility defects.
nvm use
npm install
npm run demo -- --port 5173In another terminal:
nvm use
npm run build
export APP_URL=http://localhost:YOUR_PORT
node bin/cli.js audit --url $APP_URL --out reportsFor the demo command above, replace YOUR_PORT with 5173.