by thetestingacademy
Teach agents to automate accessibility testing in CI with pa11y and pa11y-ci, including thresholds, sitemap crawling, GitHub Actions gating, and WCAG rule tuning.
npx @qaskills/cli add pa11y-accessibility-ciAuto-detects your AI agent and installs the skill. Works with Claude Code, Cursor, Copilot, and more.
You are an accessibility automation engineer who adds reliable pa11y and pa11y-ci checks to web delivery pipelines without treating scanners as a replacement for manual WCAG review.
WCAG2AA or stricter rules in config so upgrades do not silently change expectations.Install pa11y-ci into the web project.
npm install --save-dev pa11y pa11y-ci start-server-and-test
npm pkg set scripts.a11y='pa11y-ci --config .pa11yci.cjs'
npm pkg set scripts.preview='vite --host 127.0.0.1'
npm pkg set scripts.a11y:local='start-server-and-test preview http://127.0.0.1:5173 a11y'
Use a dedicated config file instead of long CLI flags.
// .pa11yci.cjs
const baseUrl = process.env.PREVIEW_URL || 'http://127.0.0.1:5173';
module.exports = {
defaults: {
standard: 'WCAG2AA',
timeout: 30000,
wait: 500,
chromeLaunchConfig: {
args: ['--no-sandbox', '--disable-dev-shm-usage'],
},
hideElements: '.third-party-chat, .cookie-banner-test-only',
},
urls: [
`${baseUrl}/`,
`${baseUrl}/pricing`,
`${baseUrl}/login`,
`${baseUrl}/dashboard`,
],
threshold: 0,
reporters: ['cli', 'json'],
};
Create a small accessibility automation area that the whole team can understand.
.
.github/
workflows/
accessibility.yml
accessibility/
urls.json
crawl-sitemap.mjs
known-issues.md
.pa11yci.cjs
package.json
Use sitemap crawling when the product has many public pages.
sitemap.xml from the preview deployment.// accessibility/crawl-sitemap.mjs
import { writeFile } from 'node:fs/promises';
const origin = process.env.PREVIEW_URL || 'http://127.0.0.1:5173';
const sitemapUrl = `${origin}/sitemap.xml`;
const response = await fetch(sitemapUrl);
if (!response.ok) {
throw new Error(`Could not fetch sitemap: ${response.status}`);
}
const xml = await response.text();
const urls = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)]
.map((match) => match[1])
.filter((url) => url.startsWith(origin))
.filter((url) => !url.includes('/logout'))
.slice(0, Number(process.env.PA11Y_MAX_URLS || 50));
await writeFile('accessibility/urls.json', `${JSON.stringify(urls, null, 2)}\n`);
console.log(`Wrote ${urls.length} URLs for pa11y-ci`);
Point the config at the generated list when required.
// .pa11yci.cjs
const urls = require('./accessibility/urls.json');
module.exports = {
defaults: {
standard: 'WCAG2AA',
timeout: 30000,
},
urls,
threshold: Number(process.env.PA11Y_THRESHOLD || 0),
};
Run the accessibility gate after the app builds and the preview server starts.
name: accessibility
on:
pull_request:
push:
branches: [main]
jobs:
pa11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- run: npm run a11y:local
- uses: actions/upload-artifact@v4
if: always()
with:
name: pa11y-results
path: pa11y-ci-results.json
Use ignore only when there is a documented reason.
module.exports = {
defaults: {
standard: 'WCAG2AA',
ignore: [
// Temporary until design system token QA-1284 is fixed.
'color-contrast',
],
},
urls: ['http://127.0.0.1:5173/'],
threshold: 0,
};
When pa11y fails, inspect the page, selector, context, and rule.
npm run a11y:local.| Need | Pa11y Choice | Agent Action |
|---|---|---|
| Small public site | Static urls list | Keep direct route coverage explicit |
| Large marketing site | Sitemap crawler | Cap URL count and exclude unsafe paths |
| Pull request gate | threshold: 0 | Fail new violations immediately |
| Legacy app | Temporary threshold | Track debt and lower threshold over time |
| Third-party widget noise | hideElements | Hide only non-product widgets |
| Rule exception | ignore | Require issue link and expiry |
threshold high forever.hideElements.WCAG2AA or stricter.- name: Install QA Skills
run: npx @qaskills/cli add pa11y-accessibility-ci12 of 29 agents supported
Build AI agents that write, run, and fix tests. Playwright, LLM evals, and CI in one live cohort.
Use code AITESTER at checkout