Configures Lighthouse CI's Accessibility category for automated accessibility testing (a11y / WCAG coverage) - `categories:accessibility` audits backed by axe-core (axe) - with per-URL minimum-score assertions (fail a build when a page's score drops below a threshold) and per-audit overrides, distinct from the Performance category that `lighthouse-perf` covers. Use when the project already runs Lighthouse CI for Web Vitals and the team wants to add accessibility coverage in the same pipeline rather than spinning up a separate scanner.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Medium
Suggest reviewing before use
Deep reference for lighthouse-a11y SKILL.md. Consult when pages need different
score bars, when mapping a failing audit ID to what it checks, or when wiring
the audit into GitHub Actions. SKILL.md keeps the single-config first run and
points here for the rest.
assert.assertions applies one threshold set to every collected URL. When
pages need different bars (a marketing homepage at 0.90, a checkout at 0.98),
use assertMatrix instead: an array where each entry pairs a
matchingUrlPattern (a regex matched against the audited URL) with its own
assertions block (lhci). assertMatrix and assertions are mutually
exclusive at the assert level, and the first matching pattern wins, so order
specific patterns before the catch-all:
// .lighthouserc.js - different a11y bars per URL
module.exports = {
ci: {
assert: {
assertMatrix: [
{
matchingUrlPattern: '.*/checkout.*',
assertions: {
'categories:accessibility': ['error', { minScore: 0.98 }],
'color-contrast': ['warn'],
},
},
{
matchingUrlPattern: '.*',
assertions: {
'categories:accessibility': ['error', { minScore: 0.90 }],
'color-contrast': ['warn'],
},
},
],
},
},
};Lighthouse's accessibility category runs a curated set of axe-core rules. The
category score (0 - 1) reflects rule pass rate weighted by severity. Common
per-audit IDs (used in assertions:):
| Audit ID | What it checks |
|---|---|
aria-allowed-attr | ARIA attributes are valid for the element's role. |
aria-hidden-body | aria-hidden not on <body>. |
aria-required-attr | Required ARIA attributes for the role are present. |
aria-required-children | Required ARIA children are present. |
aria-roles | Valid ARIA roles only. |
aria-valid-attr | ARIA attribute names are valid. |
aria-valid-attr-value | ARIA attribute values are valid. |
button-name | Buttons have accessible names. |
bypass | Skip-link or landmark for bypassing repeated content. |
color-contrast | Foreground / background contrast ≥ 4.5:1 (or 3:1 large). |
document-title | <title> is set. |
duplicate-id-active | No duplicate id on focusable elements. |
form-field-multiple-labels | Form fields don't have multiple labels. |
frame-title | <iframe> has a title attribute. |
html-has-lang | <html> has lang. |
image-alt | <img> has alt. |
label | Form fields have associated labels. |
link-name | Links have accessible names. |
list | <ul> / <ol> only contain <li>. |
listitem | <li> is inside a <ul> / <ol>. |
meta-viewport | <meta name="viewport"> doesn't disable zoom. |
tabindex | No tabindex > 0. |
valid-lang | lang attribute is valid. |
(Per lhci; full list in Lighthouse's accessibility audit documentation.)
(See the same workflow in lighthouse-perf - one workflow runs both.)
# .github/workflows/lighthouse.yml
name: lighthouse
on:
pull_request:
paths:
- 'src/**'
- 'package.json'
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npm run build
- run: npx lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- if: always()
uses: actions/upload-artifact@v4
with: { name: lighthouse-reports, path: .lighthouseci/ }