CarlasHub/accessibility-audit-plugin is a free JavaScript Action that runs an evidence-backed accessibility pre-audit for WCAG 2.2 A/AA in CI. It uses the same site-independent engine as the editor plugins and exports an accessible self-contained HTML report, a validated Excel workbook, structured JSON, CSV, SARIF 2.1.0, screenshots, and a portable ZIP.
It is an automated testing aid, not a WCAG certification. WCAG 2.2 Level AA is the conformance target; optional AAA checks are advisory. Complete the report's human assessment before making a conformance claim.
Open the public Accessibility Audit builder and follow its four focused steps: add pages, choose pages, choose a repository, then review and start. Enter one website address and review the unique same-origin pages it discovers. Likely job, category, tag, archive, utility, and query-variant pages remain visible but unchecked; the user selects the exact audit scope, up to 20 pages.
Choose Use my repository (recommended), connect GitHub, select a writable repository shared with the maintained CarlasHub GitHub App, review the setup, and choose Start accessibility audit. Visitors authorize the App and do not create an App of their own. The connector creates or updates only its managed .github/workflows/accessibility-audit.yml file and starts the Action immediately. A different workflow at the same path is never replaced. Create a repository first retains the checked pages while GitHub opens in a new tab; return to the builder, grant App access if needed, refresh repositories, and select the new repository to inject those pages and launch. Manual URL entry and Preview or download the workflow instead remain available as fallback routes.
Discovery and auditing are separate: discovery proposes pages, while the Action audits exactly the selected URLs written into the workflow and never crawls. Read the public builder guide for limits, permissions, data handling, free-plan boundaries, and recovery steps.
The shortest setup delegates the audit and report upload to the maintained reusable workflow. Add this as .github/workflows/accessibility-audit.yml in your project:
name: Accessibility audit
on:
workflow_dispatch:
permissions:
contents: read
jobs:
audit:
uses: CarlasHub/accessibility-audit-plugin/.github/workflows/reusable-accessibility-audit.yml@v1
with:
urls: |-
https://example.com/
https://example.com/contact
exact-hosts: example.com
max-pages: '2'List every page under urls, one per line, then open Actions → Accessibility audit → Run workflow and start the run. Its summary links directly to one combined downloadable report. No checkout, browser installation, artifact step, token, or hostname field is required.
The reusable workflow follows the latest compatible v1 release. For release-controlled environments, use the workflow generated by the public setup builder as a model and pin referenced Actions to immutable commit SHAs.
To create or update one audit comment, grant pull-request write access and pass the workflow token:
permissions:
contents: read
pull-requests: write
steps:
- uses: CarlasHub/accessibility-audit-plugin@v1
with:
urls: https://preview.example.test/
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-on-pr: 'true'The Action uses the GitHub REST API only to list, create, or update its marked pull-request comment. When baseline-path is set, that comment also shows new, persistent, and resolved finding counts. Persistent means the same stable fingerprint appears in both audits; partial comparisons call out findings whose browser engine, URL, or viewport scope was not equivalently observed. A missing or read-only token does not discard the audit; it emits a warning and continues. Workflows triggered from forks commonly receive a read-only token.
| Input | Default | Purpose |
|---|---|---|
urls |
Required | Explicit HTTP(S) URLs, one per line (plain, bulleted, or numbered), or a JSON string array. Every entry is validated; the Action does not crawl. |
auditor |
GitHub Actions |
Auditor name written into the workbook. |
wcag-level |
AA |
WCAG 2.2 Level AA conformance target. Legacy AAA values enable advisory AAA automation but do not change the target. |
aaa-advisory |
false |
Run supported AAA rules as clearly separated advisory evidence. |
output-dir |
accessibility-audit-results |
Output directory, relative to the workspace unless absolute. |
landing-page-url |
First URL | Report metadata and same-origin link context; it does not expand scope. |
allowed-hosts |
Hosts in urls |
Optional comma- or newline-separated hostname allowlist. Each entry authorizes that hostname and its subdomains. The Action securely derives this list from the explicit URLs when omitted. |
exact-hosts |
Empty | Optional comma- or newline-separated literal hostname allowlist. Use it when approved hosts must not authorize subdomains. |
max-pages |
Unlimited | Optional hard ceiling from 1 to 50,000 authorized, deduplicated pages. An oversized scope stops before browser startup instead of being truncated. |
staging-only |
false |
Reject hosts that do not look like staging, QA, preview, test, or local hosts. |
capture-screenshots |
true |
Retain contextual screenshot evidence when reproducible. |
browser |
chromium |
Browser engine: chromium, firefox, or webkit. Firefox and WebKit are opt-in; Chromium remains the default. |
publish-report |
false |
Reusable workflow only: explicitly publish the complete report folder as a public GitHub Pages site and expose hosted-report-url. Leave disabled for the normal access-controlled workflow artifact. |
browser-channel |
Empty | Optional installed Chromium channel such as chrome; invalid with Firefox or WebKit. |
auto-install-browser |
true |
Install the selected Playwright browser when it is unavailable. |
concurrency |
2 |
Concurrent pages, from 1 to 8. |
timeout-ms |
30000 |
Per-operation timeout in milliseconds. |
journeys |
Empty | JSON array of project-specific keyboard, form, interaction, and dynamic-content journeys. |
journeys-file |
Empty | Workspace-relative JSON file containing the journey array. Check out the caller repository first when using the standalone Action. Mutually exclusive with journeys. |
report-name |
Accessibility_Audit_Report.xlsx |
Excel report filename. |
baseline-path |
Empty | Workspace-relative path to a prior audit-results.json. The reusable workflow checks out the caller repository when this is set. |
history-paths |
Empty | Optional prior audit-results.json paths, one per line, for chronological HTML and workbook trends. The reusable workflow checks out the caller repository when this is set. |
fail-on |
none |
none, new, blockers, confirmed, critical, serious, moderate, or minor. new requires baseline-path. |
comment-on-pr |
true |
Attempt the marked pull-request summary when a token is supplied. |
github-token |
Empty | Token used only for the pull-request summary. |
Generic automation cannot know the meaningful tasks in an application. Add a version-controlled journey file to test the exact keyboard and interaction outcomes your users depend on. With the reusable workflow, the complete job remains:
jobs:
audit:
uses: CarlasHub/accessibility-audit-plugin/.github/workflows/reusable-accessibility-audit.yml@v1
with:
urls: |-
https://preview.example.test/
https://preview.example.test/contact
journeys-file: .github/a11y-journeys.jsonCreate .github/a11y-journeys.json in the caller repository:
[
{
"id": "submit-empty-contact-form",
"title": "Receive an announced error after keyboard submission",
"categories": ["keyboard", "forms", "dynamic-content"],
"steps": [
{ "action": "focus", "selector": "#submit-contact" },
{ "action": "press", "key": "Enter" },
{ "action": "assert", "expectation": "invalid", "selector": "#email" },
{ "action": "assert", "expectation": "live-region-updated", "selector": "#form-errors", "value": "Email is required" }
]
}
]Supported actions are focus, press, type, wait, and assert. Assertions cover focus, visibility, expanded/pressed/selected/checked/valid states, URL and text content, input values, and scoped live-region updates. Optional urlIncludes and viewports filters keep a journey within its intended route and layout. Every journey starts from the requested URL, records its executed steps and selectors in JSON, and appears in the HTML evidence table.
Use the maintained BuggyLand pack for complete examples covering keyboard entry, form errors, custom tabs, modal focus/Escape behavior, and status messages. A passing scripted DOM journey does not establish equivalent screen-reader output or complete WCAG conformance; retain native assistive-technology and human task testing.
The gate boundaries are explicit and stable:
| Policy | The job fails when |
|---|---|
none |
Never; the audit records evidence only. |
new |
The baseline comparison identifies at least one new finding in equivalently observed scope. |
blockers |
At least one audit blocker prevented complete coverage. |
confirmed |
At least one finding has confirmed evidence, including an Advisory finding. |
critical |
At least one confirmed finding is Critical. |
serious |
At least one confirmed finding is Serious or Critical. |
moderate |
At least one confirmed finding is Moderate, Serious, or Critical. |
minor |
At least one confirmed finding is Minor, Moderate, Serious, or Critical. |
Review and manual items never fail a severity gate because automation has not established that they are defects. Blockers fail only the blockers gate, and Advisory findings fail confirmed but do not satisfy a severity threshold. Supplying baseline-path does not change any existing gate; comparison results affect only fail-on: new.
Start with none while establishing a baseline. Move to a severity policy after the audited URLs are stable and the team has reviewed the evidence model.
To prevent regressions without making teams fix the complete backlog at once, commit a reviewed prior JSON report and enable the differential gate:
jobs:
audit:
uses: CarlasHub/accessibility-audit-plugin/.github/workflows/reusable-accessibility-audit.yml@v1
with:
urls: https://preview.example.test/
baseline-path: .github/accessibility-baseline.json
history-paths: |-
reports/august/audit-results.json
reports/september/audit-results.json
fail-on: newThe comparison qualifies incomplete or unmatched browser-engine, URL, and viewport scope as indeterminate instead of reporting it as new or resolved. History is optional and reporting-only: paths may be listed in any order, the report sorts audits chronologically, and it does not change the existing baseline gate.
The Action exposes output-dir, html-path, report-path, json-path, csv-path, sarif-path, archive-path, confirmed-findings, review-findings, blockers, requested-pages, audited-pages, completed-pages, partial-pages, not-started-pages, skipped-pages, and gate-result. An if: always() upload step preserves evidence even when the configured gate fails. Upload both output-dir and archive-path: the first supports direct artifact browsing, while the ZIP is the portable package to share. Download the workflow artifact and open Accessibility_Audit_Report.html first; keep the workbook, raw JSON, CSV register, SARIF results, and screenshots together for deeper analysis and automation.
Local CLI and plugin runs never upload reports. The reusable workflow normally keeps its report in the workflow artifact, governed by repository access and the configured retention period. For an audit that contains only content approved for public disclosure, it can instead publish a directly browsable GitHub Pages copy:
permissions:
contents: read
pages: write
id-token: write
jobs:
audit:
uses: CarlasHub/accessibility-audit-plugin/.github/workflows/reusable-accessibility-audit.yml@v1
with:
urls: https://example.com/
exact-hosts: example.com
publish-report: trueBefore enabling it, configure the caller repository's Pages source as GitHub Actions. The workflow stages a copy of the established report folder, makes its HTML report the site index, deploys it with permissions confined to the opt-in publish job, writes the public URL to the run summary, and returns it as the reusable workflow's hosted-report-url output. A later deployment to the same Pages site replaces the prior one.
GitHub Pages visibility depends on the caller repository and organization plan; do not assume the URL is private. Never enable publish-report for authenticated audits, private URLs, personal data, confidential screenshots, or any report not explicitly approved for its resulting Pages audience. Disabling the input again prevents future deployments but does not itself remove an already published site; unpublish or disable Pages in the repository settings when the public copy must be withdrawn.
The separate Native screen-reader evidence workflow can be started from the repository's Actions tab with an explicit target URL and a bounded navigation-step count. It runs Guidepup on the native combinations it supports in CI:
- VoiceOver with WebKit on macOS;
- NVDA with Firefox on Windows.
Each job uploads JSON, Markdown, HTML, and Playwright artifacts containing the spoken phrase log, browser and operating-system metadata, commands performed, page structure inventory, and limitations. These scripted journeys are supplementary evidence: a qualified tester must still evaluate meaningful announcements, dynamic states, real tasks, supported product combinations, and mobile assistive technology.
- Pin third-party Actions to complete commit SHAs and grant the workflow only the permissions it needs.
- The Action derives
allowed-hostsfromurls; setexact-hostswhen every approved hostname must match literally, or setallowed-hostsexplicitly when parent-domain-and-subdomain authorization is intended. Usemax-pagesto reject unexpectedly large explicit scopes before browser startup. Usestaging-only: 'true'where naming conventions make it reliable. - Main-page redirects are checked against the same host and staging restrictions; out-of-scope destinations are rejected and are not accepted as audit results.
- Do not put credentials, session tokens, private URLs, or secrets in
urlsor workflow logs. - Treat reports and screenshots as potentially sensitive. Set an appropriate artifact retention period and restrict repository access.
publish-reportis deliberately off by default. Enable it only after approving the complete report for the repository's GitHub Pages audience, and disable or unpublish Pages when the hosted copy is no longer needed.- Do not run untrusted pull-request changes with production credentials or network access to private targets.
- Keep manual review in the release process; no automated result proves complete WCAG conformance.
The bundled Action code and Playwright runtime are committed under action/dist so a consumer does not run npm install. Maintainers reproduce and verify that bundle with npm run build:action and npm run test:action.
The Action is designed for publicly reachable HTTP(S) pages. Sites that require authentication, CAPTCHA completion, private-network access, or anti-bot exceptions need an explicitly authorized, site-specific workflow and manual review.