Automate accessibility audits with axe-core and Playwright
Write Playwright tests that inject axe-core into each page, run a full accessibility analysis, assert there are no critical or serious violations, and output a readable violation report on failure. Integrate the check as a first-class test alongside your existing functional tests.
Why this matters
Manual accessibility audits are slow and inconsistent. axe-core running in Playwright catches 30-40% of WCAG issues automatically, runs in milliseconds, and produces machine-readable results that can block a PR. Adding accessibility checks to CI is the single highest-leverage accessibility practice a team can adopt; it prevents new violations from merging from the day it is set up.
Before you start
- Node.js with @playwright/test and @axe-core/playwright installed
- A web application to test
- Basic Playwright test writing experience
Step-by-step guide
- 1
Install and run a basic analysis
Run npm install @axe-core/playwright. Import AxeBuilder from @axe-core/playwright in your test file. Run const results = await new AxeBuilder({ page }).analyze(). Print results.violations.length. On a typical production site, expect 5-20 violations.
- 2
Write the assertion
Add expect(results.violations).toHaveLength(0). Run the test. It will probably fail; that is expected. The goal is to understand what you are asserting before deciding how to handle existing violations.
- 3
Read the violation output
For each violation, axe reports: id (the rule), impact (critical/serious/moderate/minor), help (plain-English description), and nodes (the failing elements). Print the violations array as JSON and read the first 3 carefully. Map each to a WCAG criterion.
- 4
Scope the assertion to critical and serious
Update the assertion to filter: const critical = results.violations.filter(v => v.impact === 'critical' || v.impact === 'serious'). Assert that critical has length 0. This is the pragmatic starting point; fix blocking issues first, track moderate issues separately.
- 5
Add a custom reporter
On test failure, format violations as a readable table: violation id, impact, affected element count, help URL. Write it to a file and attach it as a Playwright test attachment with testInfo.attach(). The readable report is what the developer gets when CI fails; make it actionable.
- 6
Run across all pages
Add the axe check to the beforeEach of your existing test suite, or write a dedicated accessibility spec that visits all key pages. Track the violation count per page over time. The goal is not zero violations on day one; the goal is no new violations per PR.