Skip to content

Repository files navigation

Alfa A11y CI/CD

Accessibility audit (Alfa)

A small front-end project that demonstrates how to wire Siteimprove Alfa — an open, standards-based accessibility conformance testing engine — into a GitHub Actions CI/CD pipeline.

Every push and pull request renders the site in a real browser, audits the DOM against WCAG (via Alfa's ACT rules), and fails the build if there are accessibility violations.

🚀 Want to do this in your own project?

Stack

Concern Tool
Front-end Vite + TypeScript (vanilla)
Browser automation Playwright
Accessibility @siteimprove/alfa-test-utils + @siteimprove/alfa-playwright
CI/CD GitHub Actions (.github/workflows/accessibility.yml)

Project layout

.
├── index.html                          # Accessible landing page (Vite entry)
├── src/
│   ├── main.ts                         # Progressive-enhancement JS
│   └── style.css                       # High-contrast, keyboard-friendly styles
├── tests/
│   ├── accessibility.spec.ts           # The Alfa accessibility gate
│   └── support/report.ts               # Terminal guidance + CI report artifacts
├── playwright.config.ts                # Boots the dev server + runs the audit
├── .github/workflows/accessibility.yml # The CI/CD pipeline
├── a11ycicdguideforgithub/             # The hosted step-by-step guide site (GitHub Pages)
└── docs/
    ├── CHECKLIST.md                    # Copy-paste setup checklist
    ├── CI-ACCESSIBILITY-GUIDE.md       # Full framework-agnostic guide
    └── ACCESSIBILITY.md                # How this repo's gate works

Quick start

npm install                       # install dependencies
npx playwright install chromium   # download the browser Alfa audits in
npm run dev                       # http://localhost:5173

Run the accessibility audit locally

npm run test:a11y

Playwright starts the dev server automatically and Alfa audits the rendered pages. No report files are created locally. The default enforcing mode exits non-zero when a production page fails—the same gate that runs in CI. The intentionally broken violations.html page is also scanned on every run so its report remains available, but it is explicitly non-blocking as a demonstration fixture.

Developer-focused reporting

Recent reporting changes make a failure actionable directly from the terminal. For each failed rule, npm run test:a11y prints the affected source file and line when it can be matched, the reason Alfa reported it, and a safe starting fix:

[FAILED] violations.html (2 occurrence(s))
Why: The image does not have an accessible name
Code: violations.html:41  <img src="/vite.svg" width="48" height="48" />
Change to: <img src="image.svg" alt="Describe the image purpose">

The report also includes the Alfa rule link, occurrence count, and detailed Alfa targets/diagnostics. Source-line matching is available for the file-backed markup patterns covered by the report; dynamically generated DOM still includes Alfa's target evidence and rule guidance rather than a guessed source location.

Local runs versus CI artifacts

Local runs stay file-free and print the developer guidance above. In GitHub Actions, the same audit additionally writes and uploads an accessibility-reports artifact for each audited route at reports/<route>/:

File Purpose
accessibility-report.xlsx Nine-sheet remediation workbook; Issues Overview mirrors the terminal with Title, exact Location, Why, and Fix
report.json Complete machine-readable audit evidence and Alfa outcomes
summary.md Human-readable scan summary
issues.csv Issue-level export
rules.csv Rule-level export

Artifacts are uploaded even when the audit fails, so the evidence remains available from the failed workflow run.

Other scripts:

npm run typecheck   # tsc --noEmit
npm run build       # type-check + production build

How the CI/CD pipeline works

.github/workflows/accessibility.yml runs two jobs on every push/PR to main:

  1. Type-check & build — npm ci, npm run typecheck, npm run build.
  2. Accessibility audit (Alfa) — installs Chromium, runs the Alfa audit, and uploads both the Playwright HTML report and the generated accessibility report bundle (XLSX, Markdown, JSON, and CSV) as build artifacts. A WCAG violation fails this job and therefore blocks the merge.

To enforce the gate, mark the Accessibility audit (Alfa) check as required in your branch protection / ruleset for main.

For a maintainer-only exception path or a temporary advisory rollout, see Accessibility gate overrides. Normal pull requests should remain enforcing.

How the gate works (in code)

The audit is scoped to WCAG 2.1 level AA + Alfa Best Practices + ARIA (67 rules). See tests/accessibility.spec.ts:

// A rule is included if it is WCAG 2.1 A/AA, a Best Practice, or an ARIA rule.
const conformanceTarget: typeof Rules.wcag21aaFilter = (rule) =>
  Rules.wcag21aaFilter(rule) || Rules.bestPracticesFilter(rule) || Rules.ARIAFilter(rule);

await page.goto("/");
const alfaPage = await Playwright.toPage(await page.evaluateHandle("document"));
const alfaResult = await Audit.run(alfaPage, { rules: { include: conformanceTarget } });

const failingRules = alfaResult.resultAggregates.filter((a) => a.failed > 0);
expect(failingRules.size).toBe(0);                          // fail on violations

Documentation

Doc Use it to…
Step-by-step guide site Follow a hosted, visual walkthrough
docs/CHECKLIST.md Copy-paste the setup into your project (~15 min)
docs/CI-ACCESSIBILITY-GUIDE.md Understand every piece + adapt to any framework
docs/ACCESSIBILITY.md See how the gate is configured in this repo
docs/A11Y-OVERRIDES.md Configure an audited bypass or advisory rollout

Optional: publish results to Siteimprove

Local audits and GitHub Actions run fully offline by default. To publish results to the Siteimprove Intelligence Platform, add these repository secrets (Settings → Secrets and variables → Actions → Secrets):

Secret Value
SI_USER_EMAIL Your Siteimprove account email
SI_API_KEY Your Siteimprove API key
SI_SITE_ID The target site ID

Then add a repository variable under Actions → Variables:

Variable Value
SI_UPLOAD_ENABLED true to upload; false (or delete it) to keep SIP off

When enabled, each audited route is uploaded and the job prints its direct SIP report link. A missing secret, invalid site ID, rejected upload, or unavailable Code Checker subscription fails clearly. Turning the variable off skips only the SIP upload; Alfa auditing and the CI report artifacts continue normally. Local runs remain offline unless you explicitly set the flag and credentials.

Learn more

Releases

Packages

Contributors

Languages