Integrating Accessibility QA into Your CI/CD Pipeline
Integrating Accessibility QA into Your CI/CD Pipeline
Accessibility (a11y) is essential for inclusive products, yet many teams treat it as a separate checklist that happens late in development. This tutorial shows how to integrate accessibility testing into your CI/CD pipeline so every PR and deployment is evaluated for inclusivity. You’ll learn practical strategies, concrete tooling, and a repeatable workflow you can adopt in minutes.
What you’ll build
- A lightweight, automated a11y test suite that runs on every pull request
- Clear, actionable failure signals that don’t block engineers with noisy errs
- A sustainable process for tracking accessibility issues alongside code changes
Prerequisites
- A modern JavaScript/TypeScript web app (React, Vue, Angular, or plain HTML)
- Node.js and npm/yarn installed
- A Git-based workflow with pull requests
- Basic familiarity with CI/CD tools (GitHub Actions used in examples)
Overview of the approach
- Use automated accessibility testing to catch common issues at the UI level.
- Integrate tests into CI to fail PRs when regressions appear.
- Add lightweight visual checks and manual QA gates where automated checks aren’t enough.
- Maintain an accessibility scoreboard to track progress over time.
Section 1: Choosing the right accessibility tests
- Structural checks: Ensure semantic HTML is used (correct landmarks, headings order, ARIA attributes only where needed).
- Per-element checks: Verify color contrast, focus visibility, keyboard operability.
- Interaction checks: Validate that common widgets (buttons, menus, modals) are accessible via keyboard and provide proper screen reader hints.
- Dynamic content checks: Ensure live regions and ARIA updates announce changes to assistive tech.
Recommended tooling (focused on automated checks)
- Axe-core: Core accessibility engine that runs in the browser and can be integrated into unit/integration tests.
- Axe Core CLI or axe-core with a11y automation libraries (e.g., @axe-core/puppeteer, axe-playwright).
- Pa11y or Pa11y-ci for headless browser accessibility testing.
- Lighthouse accessibility audits for performance- and UX-oriented checks.
- Color contrast checkers (e.g., accessible-color-contrast or axe plugin coverage).
Section 2: Setting up automated tests locally
Goal: get quick feedback during development and before pushing code.
1) Install dependencies
- If you’re using npm:
- npm install save-dev axe-core @axe-core/puppeteer pa11y-pa11y lighthouse
- If you’re using yarn:
- yarn add dev axe-core @axe-core/puppeteer pa11y-pa11y lighthouse
2) Add a local test script
Sample script (Node.js) to run axe against a local page:
- Create test/axe-test.js
- const puppeteer = require('puppeteer');
- const { axe } = require('axe-core');
- (simplified example) launch browser, navigate to http://localhost:3000, inject axe, run, and fail if violations found.
3) Basic usage example
- Start your app locally (e.g., npm run start)
- Run node test/axe-test.js and review violations
- Focus on high-severity violations first (contrast, missing alt text, incorrect landmark usage)
4) Add color-contrast checks
- Integrate with axe or run a separate color-contrast library to flag insufficient contrast ratios ( WCAG 2.1 AA requires at least 4.5:1 for normal text).
Section 3: Integrating into CI (GitHub Actions example)
Goal: automatically test accessibility on every PR and PR review.
1) Create a workflow file
- Path: .github/workflows/accessibility.yml
- Triggers: on pull_request, on push to main (optional)
- Jobs:
- name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js uses: actions/setup-node@v4 with: node-version: '18'
- name: Install run: npm ci
- name: Build run: npm run build
- name: Run accessibility tests run: node test/axe-test.js
- name: Run Pa11y run: npx pa11y http://localhost:3000 Note: If your app isn’t hosted in CI, you can use a headless browser approach with Puppeteer to spin up the app in CI and run tests against it, or deploy a temporary preview URL to test against.
- name: Build
runs-on: ubuntu-latest
steps:
2) Treat results as gate decisions
- Configure the workflow to fail if any violations exceed a threshold or if critical violations exist.
- Example: fail if axe findings include any with impact: critical or failure count > 0 for critical issues in a PR.
3) Optional: artifactizing results
- Save a11y results as artifacts so developers can inspect them.
- Example: upload the JSON report or generate a human-readable HTML report.
Section 4: Handling flaky tests and maintenance
- Flakiest issues: color contrast can vary with themes; ensure tests are robust by testing against a representative theme.
- Reduce noise: focus thresholds on critical items first (e.g., ensure button labels are readable, not hidden behind hover).
- De-duplicate violations by element or component to avoid repetitive failures in the same area.
Section 5: Manual QA gates and integration with design/story process
- Automated tests cover baseline issues; manual checks confirm nuanced flows.
- Create a11y checklists for reviewers in PR templates:
- Can I navigate with keyboard?
- Are all actionable controls visible and labeled?
- Do dynamic updates announce changes?
- Are images and icons properly described?
- Use design reviews or a11y champions in your team to perform periodic audits on complex components (accordions, carousels, custom widgets).
Section 6: Progressive improvements and metrics
- Track metrics over time to show progress:
- Number of accessibility violations per PR
- Time to fix for critical issues
- Coverage of components with tested a11y attributes
- Visualize progress with a simple dashboard:
- A line chart of critical violations over sprints
- A heatmap of components with consistent issues
- Set realistic targets (e.g., reduce critical violations by 75% in 6 months, maintain 0 critical violations in main branch).
Section 7: Example: integrating with a React app
1) Install dependencies
- npm install save-dev @axe-core/puppeteer puppeteer 2) Simple axe-test.js (outline)
- Start a dev server in CI, navigate to the page, run axe-core, and log results. 3) Add script to package.json
- "test:a11y": "node test/axe-test.js" 4) Update CI workflow to run npm run build && npm run test:a11y
Section 8: Example: a minimal local test using Playwright
- Install: npm i -D @playwright/test
- Create tests/a11y.spec.ts
- Use Playwright to open page, inject axe-core, run, and assert no violations
- Run: npx playwright test
Illustrative example: a simple component and an a11y check
- Component: a custom modal with an overlay
- Potential issues:
- Overlay lacks aria-hidden when modal is open
- Focus trap not implemented
- Close button lacks accessible label
- Automated checks would catch missing aria-labels, missing roles, and focus management gaps, prompting quick fixes before merge.
Common pitfalls and how to avoid them
- Pitfall: Treating a11y as a one-off task
- Solution: Treat it as an ongoing quality gate integrated into CI/CD and design reviews.
- Pitfall: Over-reliance on automated checks
- Solution: Combine automated tests with manual audits for complex components and real-world usage scenarios.
- Pitfall: Slow CI due to heavy tests
- Solution: Run lightweight checks on PRs and schedule deeper audits in nightly builds or during release cycles.
Final checklist before shipping
- Automated a11y checks fail builds on critical issues
- PRs include at least one fix for any reported critical or high-severity issues
- Manual QA covers keyboard navigation and screen reader compatibility for core flows
- Accessibility metrics are tracked and show improvement over time
- A11y champions are available for ongoing guidance and reviews
If you’d like, I can tailor this to your stack (React, Vue, or Svelte), show exact code snippets for your setup, or draft a ready-to-paste CI workflow for GitHub Actions, GitLab CI, or CircleCI. Which stack and CI/CD platform are you using?
-
Rizwan Saleem | https://rizwansaleem.co
Top comments (0)