Text alternatives (1.1.1)
missing alt, file-name or placeholder alt (IMG_1234.png, "image of…"), unnamed <svg role="img">, <input type="image">.
GitHub Packages · MIT · Node ≥ 18
WCAG 2.2 accessibility audit of static HTML for CI, with a baseline so you can adopt it on an existing site and only fail on new problems.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/a11y-wardenPublished to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.
$ npx a11y-warden test/fixtures/bada11y-warden — /workspace/a11y-warden/test/fixtures/bad2 page(s) audited HIGH index.html:1 meta viewport "width=device-width, user-scalable=no" blocks zooming [viewport-zoom-disabled] ↳ fix: Remove user-scalable=no and maximum-scale.HIGH index.html:2 <img src="hero.jpg"> has no alt [img-alt-missing] ↳ fix: Add alt text describing the image, or alt="" if it is decorative.HIGH index.html:3 <input name="q"> has no label (placeholder is not a label) [input-label-missing] ↳ fix: Add <label for="id">, wrap it in <label>, or use aria-label.HIGH index.html:3 <select name="s"> has no label [input-label-missing]
Most accessibility regressions are simple and machine-detectable: an image without alt text, an icon button with no name, a form field whose only label is its placeholder, grey-on-white text, a modal hidden with aria-hidden that still traps keyboard focus. a11y-warden reads every built HTML page, checks it against a focused set of WCAG 2.2 success criteria, and reports each problem with the file, line, severity, the criterion and a concrete fix. Record existing issues in a baseline file and CI fails only on new ones.
CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. Works on any static build output (Astro, Next.js “output: 'export'”, Eleventy, Hugo, Jekyll, Docusaurus, VitePress, plain HTML) and runs offline: point it at the folder your build writes. By Louis Wright.
missing alt, file-name or placeholder alt (IMG_1234.png, "image of…"), unnamed <svg role="img">, <input type="image">.
form controls without label/aria-label/aria-labelledby/title (placeholders do not count), buttons and links without accessible names, generic link text, iframes without title.
inline color against the nearest inline background-color (or a configurable default), with the 3:1 large-text exception.
<html lang>, <title>, a single <main>, data tables without <th>.
invalid roles, aria-labelledby/describedby/controls and <label for> pointing at missing ids, duplicate referenced ids, aria-hidden containing focusable elements, positive tabindex, inline outline: none.
viewport that disables zoom, media that autoplays with sound and no controls.
--update-baseline records current findings; later runs ignore them and report only new ones. Hidden content (hidden, aria-hidden) is skipped.
# Example
npx a11y-warden dist# record existing issues once
npx a11y-warden dist --update-baseline# Example
npx a11y-warden dist --baseline .a11y-baseline.json --fail-on medium# Example
npx a11y-warden _site --format sarif > a11y.sarifpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
a11y-warden:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- run: npm ci && npm run build # produce the static site first
- uses: MrLouisWright/a11y-warden@v1
with:
path: dist
fail-on: high
sarif: a11y-warden.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: a11y-warden.sarif }
| Flag | Description |
|---|---|
| Site | |
--base-url <url> | Public URL of the site, e.g. https://example.com (default: inferred from canonical/og:url) |
--include <globs> | Only check these pages (file or URL path globs, comma-separated) |
--exclude <globs> | Skip these pages |
--clean-urls | Pages are served without .html (about.html -> /about) |
| Baseline | |
--baseline <file> | Ignore findings recorded in this baseline file (default: .a11y-baseline.json if present) |
--update-baseline | Write all current findings to the baseline file and exit 0 |
--strict-ids | Report every duplicate id, not only referenced ones |
| Common | |
-c, --config <file> | JSON config (default: .a11y-warden.json or "a11yWarden" in package.json) |
-f, --format <fmt> | text (default) | json | sarif | markdown |
--json <file> | Also write a JSON report |
--sarif <file> | Also write SARIF 2.1.0 |
--markdown <file> | Also write Markdown (e.g. for a job summary) |
--fail-on <sev> | critical | high | medium | low | info | none (default: high) |
--list-rules | Print rules and default severities |
--no-fixes | Hide fix suggestions in text output |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.
Buy me a coffee (opens Stripe in a new tab)Secure checkout by Stripe