By the end of this guide, you will have a repeatable accessibility testing workflow that catches WCAG 2.2 violations automatically in CI and surfaces manual testing gaps before any code ships. The full setup takes around two hours on a fresh project and about 45 minutes to retrofit into an existing one.
What You'll Build
- An automated axe-core scan integrated into your Playwright test suite that runs on every pull request
- A GitHub Actions job that fails the build when critical accessibility violations are detected
- A manual testing checklist that covers keyboard navigation, screen reader behaviour, and colour contrast
- A Notion or Markdown report template that logs violations, owner, and resolution status per sprint
Prerequisites
- Node.js 20 or later and pnpm 9 (the workflow is tested on these versions)
- Playwright 1.46 or later installed in your project
- A GitHub repository with Actions enabled
- Basic familiarity with writing Playwright tests
- A screen reader for manual testing: NVDA (Windows, free), VoiceOver (macOS/iOS, built-in), or JAWS (Windows, paid)
Step 1: Install axe-core and the Playwright Integration
Why does automated scanning matter before manual testing?
Automated tools catch around 30 to 40 percent of WCAG 2.2 violations reliably and quickly. Getting those out of the way first means your manual testing time focuses on the harder, judgment-dependent issues that tools cannot detect.
Install the required packages:
pnpm add -D @axe-core/playwright axe-core
Expected result: both packages appear in your devDependencies in package.json.
Common pitfall: Do not install axe-playwright (the older community package). Use @axe-core/playwright, which is the officially maintained Deque package and supports axe-core 4.9 and later.
Step 2: Write Your First Accessibility Test
What does a baseline axe scan look like?
Create a file at tests/a11y/homepage.spec.ts. This test opens your home page and runs a full axe scan, then fails if any violations exist at the critical or serious impact level.
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Homepage accessibility', () => {
test('should have no critical or serious violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
.analyze();
const blockers = results.violations.filter(
(v) => v.impact === 'critical' || v.impact === 'serious'
);
expect(blockers).toEqual([]);
});
});
Run the test locally first:
pnpm exec playwright test tests/a11y/
Expected result: the test passes or fails with a list of violations printed to the terminal. A fresh Create React App scaffold typically produces two to four violations on the first run, mostly related to missing landmark regions.
Pro tip: Target the wcag22aa tag set. WCAG 2.2 became a W3C recommendation in October 2023 and is now the baseline enforced by accessibility regulations in Australia (WCAG 2.1 AA under the DDA), Canada (EN 301 549), and the US (Section 508, also referencing WCAG 2.0 AA at a minimum). Targeting 2.2 AA keeps you ahead of all four markets.
Step 3: Scope Scans to Specific Components
When should you scope instead of scanning the whole page?
Whole-page scans are good for catching global issues like missing skip links or landmark structure. Component-scoped scans are better for testing a modal, a form, or a data table in isolation, because they give you precise violation locations and reduce noise from the rest of the layout.
// Scoped scan on a specific region
const results = await new AxeBuilder({ page })
.include('#checkout-form')
.withTags(['wcag22aa'])
.analyze();
Add scoped tests for every interactive component: navigation menus, modals, carousels, and date pickers. These are the highest-risk components for keyboard and screen reader failures.
Step 4: Add the CI Job in GitHub Actions
What if the accessibility job adds too much time to the pipeline?
The axe scan itself takes under three seconds per page on a GitHub-hosted runner. The main cost is browser startup. Run accessibility tests in the same Playwright job as your end-to-end tests to share the browser context and keep total CI time under five minutes for most projects.
Create or update .github/workflows/ci.yml:
name: CI
on:
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium
- name: Run accessibility tests
run: pnpm exec playwright test tests/a11y/
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
Expected result: every pull request now blocks merge if critical or serious WCAG 2.2 violations are detected. The Playwright HTML report is uploaded as an artifact on failure so reviewers can inspect exactly which elements failed and why.
Common pitfall: Using actions/upload-artifact@v3 will raise a deprecation warning on GitHub Actions as of mid-2025. Use v4 from the start.
Step 5: Build Your Manual Testing Checklist
Why can't automated tools cover everything?
Automated tools cannot judge whether an image description is meaningful, whether a form error message is understandable, or whether a custom widget behaves predictably under a screen reader. The WCAG 2.2 criteria most likely to be missed by automation are 2.4.11 (Focus Appearance), 2.5.8 (Target Size), and 3.3.7 and 3.3.8 (Accessible Authentication).
Add this checklist to your sprint definition of done. Assign it to a QA role or rotate it among developers.
Keyboard navigation (no mouse):
- Tab through every interactive element on the page. Focus must be visible at all times (WCAG 2.4.11 requires a minimum focus indicator area).
- Activate all buttons, links, and form controls with Enter or Space.
- Close all modals and dropdowns with Escape.
- Verify no keyboard trap exists (WCAG 2.1.2).
Screen reader testing (NVDA with Firefox, or VoiceOver with Safari):
- Read the page top to bottom using arrow keys. The reading order must match the visual order.
- Verify all images have meaningful alt text or are marked
aria-hidden="true"if decorative. - Verify form labels are announced correctly and error messages are linked to inputs via
aria-describedby. - Test any dynamic content updates (toasts, loading states) for live region announcements.
Colour contrast:
- Check all text against its background using the WebAIM Contrast Checker or the built-in DevTools colour picker. Normal text requires a 4.5:1 ratio. Large text (18pt or 14pt bold) requires 3:1.
- Check focus indicator contrast separately (WCAG 3.1 AA requires at least 3:1 against adjacent colours).
Step 6: Set Up a Violation Tracking Template
How do you keep accessibility debt from building up between sprints?
Treat violations the same way you treat bugs. Log them in a shared tracker with an owner and a target sprint. A simple Notion database or a Markdown file in your repository works well for small teams.
Create docs/accessibility-log.md with this structure:
# Accessibility Violation Log
| ID | Page / Component | Rule ID | Impact | Description | Owner | Sprint | Status |
|----|-----------------|---------|--------|-------------|-------|--------|--------|
| A-001 | /checkout | label | serious | Input missing label | Dev A | 2026-Q4-S1 | Open |
| A-002 | Modal | color-contrast | serious | Button text 2.8:1 | Design B | 2026-Q4-S1 | Fixed |
Review this log in every sprint planning session. Target zero critical and serious violations before each production release. Moderate and minor violations can be scheduled into a dedicated accessibility sprint each quarter.
Pro tip: If you work with a design team, share violation reports with them in Figma as annotation comments. Catching contrast and label issues at the design stage costs a fraction of what it costs to fix them after implementation. At Lenka Studio, we build accessibility checks into the design review stage so violations rarely reach the dev handoff.
Step 7: Add axe-core to Your Storybook for Component-Level Coverage
Why run axe inside Storybook as well as Playwright?
Storybook 8 ships with the @storybook/addon-a11y addon, which runs axe-core in the browser panel as you interact with each story. This gives designers and developers instant feedback without needing to run Playwright at all during development.
pnpm add -D @storybook/addon-a11y
Add it to your Storybook config in .storybook/main.ts:
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y', // add this
],
};
export default config;
Expected result: every story in Storybook now shows an Accessibility tab with pass, violation, and incomplete categories. Developers can fix violations before a PR is even opened, reducing the number of CI failures significantly.
Step 8: Schedule a Quarterly Full Audit
Automated scans and sprint checklists catch the majority of issues. A full audit once per quarter catches regressions, newly shipped features, and third-party widget violations that CI cannot reach (for example, an embedded payment iframe or a live chat widget).
A quarterly audit for a ten-page marketing site or a twenty-screen app takes roughly four to six hours when you have a workflow already in place. Use the WCAG 2.2 quick reference as your checklist. Document findings in your violation log and assign them before the next sprint cycle starts.
If you are preparing for a formal audit by a third-party firm (common for Australian government contractors or Canadian public-sector suppliers), this workflow gives you a clean evidence trail to share with auditors.
Teams at Lenka Studio use this same process when delivering accessibility-compliant builds for clients across Australia and Singapore, where procurement requirements are increasingly explicit about WCAG conformance levels.
Frequently Asked Questions
Does this workflow work for React Native or mobile apps?
This guide is specific to web. For React Native, the equivalent tools are eslint-plugin-jsx-a11y for static analysis and manual testing with TalkBack (Android) and VoiceOver (iOS). Playwright does not test native mobile apps.
What if axe-core flags violations in a third-party embed I can't fix?
Use the .exclude() method on the AxeBuilder instance to skip iframes or containers you do not control. Document the exclusion in your violation log so auditors understand the scope boundary.
How is this different from just using Lighthouse accessibility scores?
Lighthouse runs a subset of axe-core checks and reports a score, but it does not fail your build and it does not give you structured violation data by WCAG criterion. The axe-core Playwright integration gives you actionable rule IDs, affected elements, and impact levels you can act on directly.
Will this catch all WCAG 2.2 AA violations?
No. As of 2026, automated tools reliably detect around 30 to 40 percent of WCAG criteria. The remaining criteria require human judgment, screen reader testing, and cognitive evaluation. This workflow is designed to combine both automated and manual coverage for the broadest practical reach.
How long does the GitHub Actions job take per run?
For a typical 10-to-20 page site, the accessibility job adds roughly 60 to 90 seconds to your CI pipeline when shared with the existing Playwright end-to-end job. Running it as a standalone job adds closer to three to four minutes due to browser install overhead.
Next Steps
You now have automated scans running in CI, a manual testing checklist in your sprint process, a Storybook integration for component-level feedback, and a violation log to track remediation over time. The next layer to add is user testing with disabled participants, which surfaces usability issues that neither axe-core nor checklists will find.
If your team needs help building accessibility into your design system or engineering process from the start, get in touch with the team at Lenka Studio. We work with product teams in Australia, Singapore, Canada, and the US to ship accessible interfaces that meet real compliance requirements.




