TL;DR
Add a workflow at
.github/workflows/accessibility.ymlAdd
lighthouserc.jsonwith your URLs and assertionsCheckout, build your project (Hugo, Symfony, …), then run Lighthouse CI — via
npx --yes @lhci/cli@0.15.1 autorunor the treosh/lighthouse-ci-actionRun the same checks locally before push via npx
Info
Keep in mind that URLs used in the examples are specific for my testing applications. Your applications might have other routes you want to test with lightouse.
Example for Hugo Projects
Github Workflow
name: Accessibility
on:
push:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c #v6.4.0
with:
go-version-file: go.mod
cache: true
- uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 #v3.2.1
with:
extended: true
- name: Build site
run: hugo build --minify --gc
- name: Lighthouse accessibility
uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 #v12.6.2
with:
configPath: ./lighthouserc.json
lighthouserc.json
{
"ci": {
"collect": {
"staticDistDir": "./public",
"url": [
"http://localhost/",
"http://localhost/about/",
"http://localhost/nginx-config-tests/"
],
"numberOfRuns": 1,
"settings": {
"onlyCategories": ["accessibility"]
}
},
"assert": {
"assertions": {
"categories:accessibility": ["error", { "minScore": 1 }],
"color-contrast": "error"
}
}
}
}
How it works
staticDistDirstarts a file server that is used by lighthouse. Because the build artifacts of Hugo are in public, we point to that direction.- I chose the
urlarray so that I test articles and pages that use all of the different features of my theme like quotes, images, codeblocks. In consequence, all kinds of content display are covered regarding the accessibility check. onlyCategories“accessibility” does, what I want - check the accessibility criteria with lighthouse. You can also use the other categories of lighthouse (seo, performance, best-practices)assert: what I want to assert
Run locally with npx
Same command as in CI — build first, then invoke Lighthouse CI with a pinned version:
hugo build --minify --gc
npx --yes @lhci/cli@0.15.1 autorun
Example for Symfony Projects
Info
Please notice that I use a dockerized setup with justfile to keep things convenient. Your setup might differ, so you might need to adjust the workflow regarding commands. For example just compose pull is docker compose pull. If you run without docker, you do not need that and just have to make sure you start up your Symfony app, so the routes configured for lighthouse are accessible during the CI run, so lighthouse tests can be performed.
Github Workflow
name: Accessibility
on:
push:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- name: Install Just
uses: extractions/setup-just@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
- name: Pulling Docker images
run: just compose pull
- name: Building Docker images
run: just compose build
- name: Install dependencies
run: just install
- name: Start application
run: just init-a11y
- name: Lighthouse accessibility
uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 #v12.6.2
with:
configPath: ./lighthouserc.json
lighthouserc.json
{
"ci": {
"collect": {
"url": [
"http://localhost:8080/"
],
"numberOfRuns": 1,
"settings": {
"onlyCategories": ["accessibility"]
}
},
"assert": {
"assertions": {
"categories:accessibility": ["error", { "minScore": 1 }],
"color-contrast": "error"
}
}
}
}
My Symfony app runs at localhost:8080 (nginx-unpriviledged). Thus, this url is used in the configuration.
Execute lighthouse locally via justfile recipe
Tip
If you are not familiar with just, check out my related article on just or the official documentation. Regarding the commands below, the variables defined at the start can be substituted in the recipes 1:1.
For example {{PHP-DB-RUN}} === docker compose -f docker/compose.yml run --rm php-fpm.
COMPOSE := 'docker compose -f docker/compose.yml'
COMPOSE-RUN := COMPOSE + ' run --rm'
PHP-DB-RUN := COMPOSE-RUN + ' php-fpm'
[private]
init-a11y *args:
APP_ENV=prod {{COMPOSE}} up -d {{args}}
{{DB-RUN}} mysqladmin -uroot -pChangeMe -hdb --wait=10 ping
APP_ENV=prod {{PHP-DB-RUN}} bin/console cache:warmup --env=prod
APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:database:drop --force --if-exists --env=prod
APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:database:create --env=prod
APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:schema:create --env=prod
APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:migrations:sync-metadata-storage --no-interaction --env=prod
APP_ENV=prod {{PHP-DB-RUN}} bin/console doctrine:migrations:version --add --all --no-interaction --env=prod
# run lighthouse accessibility checks against the home page
a11y:
#!/usr/bin/env bash
set -euo pipefail
if ! curl -sf http://localhost:8080/ > /dev/null || curl -s http://localhost:8080/drives | grep -q 'sf-toolbar'; then
echo "Starting application in prod mode for accessibility audit..."
just init-a11y
fi
npx --yes @lhci/cli@0.15.1 autorun
Differences between Hugo and Symfony
The Lighthouse configuration is similar; the surrounding setup is not.
Hugo is static. You build HTML into public/ and point Lighthouse at that folder via staticDistDir. Lighthouse starts its own file server — no app process, no database, no Docker. CI is essentially: checkout → build Hugo → run Lighthouse.
Symfony is dynamic. Pages are rendered at request time, so Lighthouse needs a running application. Your lighthouserc.json lists live URLs (for example http://localhost:8080/) and CI must boot the stack first — containers, dependencies, database schema, cache warmup. That is heavier, but it audits what users actually get, including server-rendered markup and runtime-served assets.
Running Lighthouse: GitHub Action vs npx
Two approaches appear in this article; both call the same @lhci/cli under the hood.
GitHub Action (treosh/lighthouse-ci-action) | npx (npx --yes @lhci/cli@0.14.0 autorun) | |
|---|---|---|
| Used here | CI | local runs |
| Pros | Chrome and Lighthouse preconfigured; uploadArtifacts stores HTML reports on failure; no install step in the workflow YAML | No package.json or lockfile for Lighthouse; version pinned in the command |
| Cons | Extra abstraction; local runs need npx (or npm) to mirror CI | Downloads the CLI on cold runs; you must keep the @version suffix in sync everywhere you use it |
Practical takeaway: There are three ways to use lighthouse-ci. You can run it directly via npx (which you could also do in CI). Secondly, you can use the GitHub action for CI (community developed) and rely on npx locally. As third option, you can also require @lhci/cli as dev dependency and add an npm script to run lighthouse-ci (see below).
Alternative: install via npm
Instead of npx, add @lhci/cli as a devDependency and a script in package.json, then all you need is to run npm run a11y to execute lighthouse:
"scripts": { "a11y": "hugo build --minify --gc && lhci autorun" },
"devDependencies": { "@lhci/cli": "0.15.1" }
The benefit of this approach is version control via the lockfile and faster repeat runs when cached. But you pay for it with pulling in the Node toolchain, even if the site itself is not a Node app. I would recommend the approach via npm, if you already maintain a package.json for other tooling.
Advanced Usage
Apart from accessibility checks, the lighthouse package also provides the other lighthouse checks (seo, best-practices, performance).
You can use uploadArtifacts in your GitHub workflow to see a nice html report of the lighthouse checks.
Apart from checks on code, there also is a community project that offers a Lighthouse CI Compare Action, which compares the current commit against the ancestor commit and creates a nice reporting.
Sources & Further Reading
- lighthouse-ci GitHub repo: https://github.com/GoogleChrome/lighthouse-ci
- Getting started quide: https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/getting-started.md
- npm package: https://www.npmjs.com/package/@lhci/cli
- GitHub action: https://github.com/treosh/lighthouse-ci-action
- Github compare action: https://github.com/adevinta/actions-lighthouseci-compare
- GitHub actions general documentation: https://docs.github.com/en/actions
