Accessibility Testing Automation: axe-core, Lighthouse, and CI Integration

Accessibility isn't a final polish step—it's a quality attribute that must be verified throughout development. Automated accessibility testing catches the low-hanging fruit: missing alt text, insufficient color contrast, incorrect ARIA attributes, and keyboard traps. Manual testing handles the rest, but automation catches regressions on every commit.
axe-core: The Industry Standard Engine
axe-core powers most modern accessibility testing tools. It runs as a JavaScript library in your test runner or browser, injecting tests and returning structured results:
import { AxeBuilder } from '@axe-core/playwright';
import { test, expect } from '@playwright/test';
test('homepage has no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
Each violation includes the impact level (critical, serious, moderate, minor), a description, the failing HTML, and guidance for fixing it. Set a threshold in CI that fails on critical or serious violations while reporting moderate issues for awareness.
test('checkout flow accessibility', async ({ page }) => {
await page.goto('/checkout');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice'])
.analyze();
const critical = results.violations.filter(v => v.impact === 'critical');
const serious = results.violations.filter(v => v.impact === 'serious');
expect(critical).toEqual([]);
expect(serious.length).toBeLessThan(3); // Allow known, documented exceptions
});
Lighthouse CI for Accessibility Audits
Lighthouse provides an accessibility score alongside detailed audit results. Integrate it into CI with assertions on the accessibility category:
{
"ci": {
"collect": {
"numberOfRuns": 1
},
"assert": {
"assertions": {
"categories:accessibility": ["error", { "minScore": 0.95 }]
}
}
}
}
Run Lighthouse against every pull request preview deployment. The report links directly to the failing audits with code snippets and documentation. Developers fix issues before merging, not after.
For complex single-page apps, run axe-core on each route individually. Lighthouse audits only the initial page state—interactive components like modals and expandable sections need explicit axe analysis.
Automated Keyboard Navigation Tests
Write Playwright tests that verify keyboard operability:
test('navigation is fully keyboard-operable', async ({ page }) => {
await page.goto('/');
// Tab through the entire page
for (let i = 0; i < 50; i++) {
const focused = await page.evaluate(() => {
const el = document.activeElement;
return el ? el.tagName + (el.getAttribute('role') ? `[role="${el.getAttribute('role')}"]` : '') : null;
});
// Verify we didn't get stuck
expect(focused).not.toBeNull();
// Verify focus is visible
const hasOutline = await page.evaluate(() => {
const el = document.activeElement;
if (!el) return false;
const style = getComputedStyle(el);
return style.outlineStyle !== 'none' || style.boxShadow !== 'none';
});
expect(hasOutline).toBeTruthy();
await page.keyboard.press('Tab');
}
});
Test that focus moves logically through interactive elements and that modals trap focus correctly. A common failure pattern is focus jumping to a hidden element or disappearing entirely after an interaction.
Color Contrast Automation
Contrast issues are the most common accessibility violations. Automate checks on your design tokens:
import { colord } from 'colord';
const tokens = {
'text-primary': '#1a1a1a',
'text-muted': '#737373',
'bg-primary': '#ffffff',
'brand-500': '#0ea5e9',
};
function checkContrast(foreground, background, label) {
const ratio = colord(foreground).contrast(colord(background));
const passesAA = ratio >= 4.5;
const passesAAA = ratio >= 7;
if (!passesAA) {
console.error(`FAIL: ${label} (${foreground} on ${background}) ratio ${ratio.toFixed(2)} — needs 4.5`);
}
return { label, ratio, passesAA, passesAAA };
}
checkContrast(tokens['text-primary'], tokens['bg-primary'], 'Primary text on white');
checkContrast(tokens['text-muted'], tokens['bg-primary'], 'Muted text on white');
Run this as part of your lint step. If a designer updates a shade, the CI check validates the new contrast ratios immediately.
Integrating Accessibility into the Sprint
Automation alone isn't enough—it catches about 30% of WCAG violations. The rest require manual testing. Structure your a11y practice as a pyramid:
- Automated checks (axe + Lighthouse): Every PR, every deploy
- Component-level focus testing: Keyboard + screen reader (VoiceOver/NVDA)
- Flow-level manual audit: One full session per sprint
- User testing with disabled participants: Quarterly, indispensable
Use eslint-plugin-jsx-a11y for in-IDE feedback during development:
{
"extends": ["plugin:jsx-a11y/recommended"],
"plugins": ["jsx-a11y"]
}
This catches missing alt text, incorrect heading hierarchy, and non-semantic click handlers before code leaves the editor.
Building an Accessibility Regression Safety Net
Set up a CI workflow that runs axe-core against every route on every PR:
# .github/workflows/a11y.yml
name: Accessibility
on: [pull_request]
jobs:
axe:
runs-on: ubuntu-latest
services:
app:
image: preview-${{ github.sha }}
ports: ['3000:3000']
steps:
- uses: actions/checkout@v4
- run: npx playwright install
- run: npx playwright test tests/a11y/
- uses: actions/upload-artifact@v4
if: failure()
with:
name: a11y-reports
path: reports/
When a violation appears, the build fails and the report is available for review. Over time, the pattern of violations shrinks, and your baseline accessibility improves permanently.
Accessibility automation won't make a site perfectly accessible—but it ensures that every deploy is at least as accessible as the last, and that new features don't introduce the most common, most impactful violations.
Our custom UI/UX design team builds accessible interfaces from the start and integrates automated a11y testing into every project's delivery pipeline.
Related Insights

Accessibility by Design: Building Inclusive Digital Products from Day One
Build accessible digital products from day one with inclusive design principles, WCAG compliance strategies, and practical implementation patterns.

Building Accessible React Applications: WCAG 2.2 Compliance Guide
A guide to building WCAG 2.2 compliant React applications including semantic HTML, ARIA attributes, keyboard navigation, focus management, and automated accessibility testing.

Building AI Chatbots for Customer Support: A Complete Technical Guide
A technical guide to building AI-powered customer support chatbots including LLM integration, RAG architecture, conversation design, escalation workflows, and performance monitoring.