A11y Checker CI
Automated accessibility testing in CI/CD pipelines with comprehensive reporting.
Overview
To enforce accessibility standards in continuous integration, this skill configures automated WCAG compliance checks using industry-standard tools and generates detailed reports for every pull request.
When to Use
Use this skill when:
- •Adding accessibility testing to CI/CD pipelines
- •Enforcing WCAG compliance in automated builds
- •Generating accessibility reports for pull requests
- •Setting up quality gates based on accessibility
- •Automating accessibility audits
- •Tracking accessibility improvements over time
- •Ensuring new features meet accessibility standards
Supported Tools
@axe-core/playwright
Industry-standard accessibility testing engine with Playwright integration.
Advantages:
- •Comprehensive WCAG rule coverage
- •Fast execution in parallel with E2E tests
- •Detailed violation reporting
- •Active maintenance and updates
pa11y-ci
Command-line accessibility testing tool for multiple URLs.
Advantages:
- •Simple configuration
- •Standalone execution (no browser automation needed)
- •Multiple URL scanning
- •Custom rule configuration
Implementation Steps
1. Choose Testing Approach
To select the appropriate tool:
Use @axe-core/playwright when:
- •Already using Playwright for E2E tests
- •Need integration with existing test suites
- •Want to test dynamic/authenticated pages
- •Require detailed test context
Use pa11y-ci when:
- •Need simple URL-based scanning
- •Want standalone accessibility checks
- •Testing static pages or public URLs
- •Prefer configuration-based approach
2. Install Dependencies
For @axe-core/playwright:
npm install -D @axe-core/playwright
For pa11y-ci:
npm install -D pa11y-ci
3. Create Test Configuration
Option A: @axe-core/playwright
Create test file using assets/a11y-test.spec.ts:
import { test, expect } from '@playwright/test'
import AxeBuilder from '@axe-core/playwright'
test.describe('Accessibility Tests', () => {
test('homepage meets WCAG standards', async ({ page }) => {
await page.goto('/')
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze()
expect(accessibilityScanResults.violations).toEqual([])
})
})
Option B: pa11y-ci
Create configuration using assets/pa11y-config.json:
{
"defaults": {
"timeout": 30000,
"chromeLaunchConfig": {
"executablePath": "/usr/bin/chromium-browser",
"args": ["--no-sandbox"]
},
"standard": "WCAG2AA",
"runners": ["axe", "htmlcs"],
"ignore": []
},
"urls": [
"http://localhost:3000",
"http://localhost:3000/entities",
"http://localhost:3000/timeline"
]
}
4. Generate Report Script
Create report generator using scripts/generate_a11y_report.py:
python scripts/generate_a11y_report.py \ --input test-results/a11y-results.json \ --output accessibility-report.md \ --format github
The script generates markdown reports with:
- •Executive summary with pass/fail status
- •Violation count by severity (critical, serious, moderate, minor)
- •Detailed violation list with:
- •Rule ID and description
- •WCAG criteria
- •Impact level
- •Affected elements
- •Remediation guidance
- •Historical comparison (if available)
5. Configure CI Pipeline
GitHub Actions
Use template from assets/github-actions-a11y.yml:
name: Accessibility Tests
on:
pull_request:
branches: [main, master]
push:
branches: [main, master]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Start server
run: npm start &
- name: Wait for server
run: npx wait-on http://localhost:3000 -t 60000
- name: Run accessibility tests
run: npm run test:a11y
- name: Generate report
if: always()
run: |
python scripts/generate_a11y_report.py \
--input test-results/a11y-results.json \
--output accessibility-report.md \
--format github
- name: Comment PR
if: github.event_name == 'pull_request' && always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs')
const report = fs.readFileSync('accessibility-report.md', 'utf8')
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: report
})
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: accessibility-report
path: |
accessibility-report.md
test-results/
- name: Fail on violations
if: failure()
run: exit 1
GitLab CI
Use template from assets/gitlab-ci-a11y.yml:
accessibility-test:
stage: test
image: mcr.microsoft.com/playwright:v1.40.0-focal
script:
- npm ci
- npm run build
- npm start &
- npx wait-on http://localhost:3000 -t 60000
- npm run test:a11y
- python scripts/generate_a11y_report.py
--input test-results/a11y-results.json
--output accessibility-report.md
--format gitlab
artifacts:
when: always
paths:
- accessibility-report.md
- test-results/
reports:
junit: test-results/junit.xml
only:
- merge_requests
- main
6. Add Package Scripts
Add to package.json:
{
"scripts": {
"test:a11y": "playwright test a11y.spec.ts",
"test:a11y:ci": "playwright test a11y.spec.ts --reporter=json",
"pa11y": "pa11y-ci --config .pa11yci.json"
}
}
Report Format
Executive Summary
# Accessibility Test Report **Status:** [ERROR] Failed **Total Violations:** 12 **Pages Tested:** 5 **WCAG Level:** AA **Date:** 2025-01-15 ## Summary by Severity - [CRITICAL] Critical: 2 - [SERIOUS] Serious: 5 - [MODERATE] Moderate: 3 - [MINOR] Minor: 2
Violation Details
## Violations ### [CRITICAL] Critical (2) #### 1. Form elements must have labels (form-field-multiple-labels) **WCAG Criteria:** 3.3.2 (Level A) **Impact:** Critical **Occurrences:** 3 elements **Description:** Form fields should have exactly one associated label element. **Affected Elements:** - Line 45: `<input type="text" name="entity-name">` - Line 67: `<input type="email" name="user-email">` - Line 89: `<select name="entity-type">` **How to Fix:** Add a `<label>` element with a `for` attribute matching the input's `id`: \`\`\`html <label for="entity-name">Entity Name</label> <input id="entity-name" type="text" name="entity-name"> \`\`\` **More Info:** https://dequeuniversity.com/rules/axe/4.7/label ---
Historical Comparison
## Progress | Metric | Previous | Current | Change | |--------|----------|---------|--------| | Total Violations | 15 | 12 | [OK] -3 | | Critical | 3 | 2 | [OK] -1 | | Serious | 7 | 5 | [OK] -2 | | Moderate | 4 | 3 | [OK] -1 | | Minor | 1 | 2 | [ERROR] +1 |
Quality Gates
Blocking Violations
To fail builds on specific violations, configure thresholds:
const results = await new AxeBuilder({ page }).analyze()
// Fail on any critical violations
const critical = results.violations.filter(v => v.impact === 'critical')
expect(critical).toHaveLength(0)
// Allow up to 5 moderate violations
const moderate = results.violations.filter(v => v.impact === 'moderate')
expect(moderate.length).toBeLessThanOrEqual(5)
Configuration File
Use assets/a11y-thresholds.json:
{
"thresholds": {
"critical": 0,
"serious": 0,
"moderate": 5,
"minor": 10
},
"allowedViolations": [
"color-contrast"
],
"ignoreSelectors": [
"#third-party-widget",
"[data-testid='external-embed']"
]
}
Advanced Configuration
Custom Rules
To disable or configure specific rules:
const results = await new AxeBuilder({ page })
.disableRules(['color-contrast'])
.withRules({
'custom-rule': { enabled: true }
})
.analyze()
Page-Specific Tests
Test different page types:
const pages = [
{ url: '/', name: 'Homepage' },
{ url: '/entities', name: 'Entity List' },
{ url: '/timeline', name: 'Timeline View' }
]
for (const { url, name } of pages) {
test(`${name} accessibility`, async ({ page }) => {
await page.goto(url)
const results = await new AxeBuilder({ page }).analyze()
expect(results.violations).toEqual([])
})
}
Authenticated Pages
Test pages requiring authentication:
test.use({ storageState: 'auth.json' })
test('dashboard accessibility', async ({ page }) => {
await page.goto('/dashboard')
const results = await new AxeBuilder({ page }).analyze()
expect(results.violations).toEqual([])
})
Report Customization
Custom Templates
Create custom report templates in assets/report-templates/:
- •
github-template.md- GitHub PR comments - •
gitlab-template.md- GitLab MR comments - •
slack-template.md- Slack notifications - •
html-template.html- HTML reports
Report Destinations
Configure report distribution:
python scripts/generate_a11y_report.py \ --input results.json \ --output-dir reports/ \ --formats github gitlab slack html \ --slack-webhook $SLACK_WEBHOOK \ --github-token $GITHUB_TOKEN
Monitoring and Tracking
Historical Data
Store results for trend analysis:
# Save results with timestamp python scripts/save_a11y_results.py \ --input test-results/a11y-results.json \ --database a11y-history.db # Generate trend report python scripts/generate_trend_report.py \ --database a11y-history.db \ --days 30 \ --output a11y-trends.md
Metrics Dashboard
Generate metrics for dashboards:
{
"timestamp": "2025-01-15T10:30:00Z",
"commit": "abc123",
"branch": "feature/new-ui",
"violations": {
"critical": 2,
"serious": 5,
"moderate": 3,
"minor": 2
},
"wcagCompliance": {
"a": false,
"aa": false,
"aaa": false
},
"pagesTested": 5,
"totalElements": 1247,
"testedElements": 1247
}
Resources
Consult the following resources for detailed information:
- •
scripts/generate_a11y_report.py- Report generator - •
scripts/save_a11y_results.py- Historical data storage - •
scripts/generate_trend_report.py- Trend analysis - •
assets/a11y-test.spec.ts- Playwright test template - •
assets/pa11y-config.json- pa11y-ci configuration - •
assets/github-actions-a11y.yml- GitHub Actions workflow - •
assets/gitlab-ci-a11y.yml- GitLab CI configuration - •
assets/a11y-thresholds.json- Violation thresholds - •
references/wcag-criteria.md- WCAG standards reference - •
references/common-violations.md- Common issues and fixes
Best Practices
- •Run accessibility tests on every pull request
- •Set appropriate thresholds for violations
- •Generate readable reports for developers
- •Track accessibility metrics over time
- •Test authenticated and dynamic pages
- •Include accessibility in definition of done
- •Review and update ignored rules periodically
- •Provide remediation guidance in reports
- •Celebrate accessibility improvements