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.
- Step-by-step guide site — the hosted, visual walkthrough (GitHub Pages)
- Copy-paste checklist — ~15 minutes, top to bottom
- Full CI accessibility guide — every piece explained, for React, Next.js, Vue, Angular, Svelte, Astro, static sites, or a deployed preview URL
| 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) |
.
├── 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
npm install # install dependencies
npx playwright install chromium # download the browser Alfa audits in
npm run dev # http://localhost:5173npm run test:a11yPlaywright 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.
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 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.github/workflows/accessibility.yml runs two jobs on every push/PR to main:
- Type-check & build —
npm ci,npm run typecheck,npm run build. - 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.
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| 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 |
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.
- Alfa engine — https://github.com/siteimprove/alfa
- Accessibility Code Checker docs — https://alfa.siteimprove.com/code-checker
- Worked examples — https://github.com/Siteimprove/alfa-examples