To fail a GitHub merge on new accessibility regressions you need three pieces: an accessibility GitHub Action that runs on pull_request and exits non-zero when it finds something new, a baseline of known issues saved from your default branch, and a branch protection rule (or ruleset) that marks that job as a required status check. GitHub only lets a pull request merge into a protected branch once each required check reports a "successful, skipped, or neutral" status (GitHub Docs), so a red accessibility job becomes a real merge block instead of a suggestion. The baseline is what keeps the gate livable: the job fails on findings the pull request introduced, not on the 300 issues that were already there last year. Below I set this up with our own Action, WCAG-Compliance/wcagc-ci@v1, and cover the parts that trip teams up: what counts as "new", fork pull requests, and which URLs to point it at. One limit up front. Automated checks find a share of WCAG issues, not all of them, so a green check means no new machine-detectable regressions. Conformance still needs a human.
What does an accessibility GitHub Action actually block?
It blocks the pull request that makes things measurably worse. The Action sends a list of URLs to a scanner, waits for the results, compares them with a baseline, and turns that comparison into a verdict: PASS or FAIL. On FAIL the step exits with code 1, the job goes red, and if the job is required, the merge button stays disabled.
What it does not block is anything the engine can't see. Deque's study of more than 2,000 audits found automated testing covered 57% of issues by volume (Deque, 2021), and the older industry benchmark people quote is 20 to 30%. So think of the gate as a regression guard for the machine-checkable half of WCAG. It is good at catching an <input> that lost its label in a refactor. It has no opinion on whether your new onboarding flow makes sense with a screen reader.
If you want the broader tool comparison (axe-core in Playwright, pa11y-ci, Lighthouse CI), I wrote that up in accessibility testing in CI. This post is about the merge gate itself.
How do I set up the accessibility GitHub Action?
You need two workflows. One runs on every pull request and judges it. The other runs on pushes to main and saves the result as the new baseline.
Before either works, register and verify the domain in wcagc, create an organization API key with the ci:check scope, and store it as the repository secret WCAGC_API_KEY. All URLs in one run must sit on that one verified host (wcagc-ci README). The free plan includes 30 checks a month with up to 5 URLs each, which is enough to try this on a small repo.
The pull request workflow:
name: Accessibility
on: [pull_request]
jobs:
a11y-gate:
runs-on: ubuntu-latest
steps:
- uses: WCAG-Compliance/wcagc-ci@v1
with:
api-key: ${{ secrets.WCAGC_API_KEY }}
urls: |
https://staging.example.com/
https://staging.example.com/signup
https://staging.example.com/checkout
fail-on: new-critical
The baseline workflow, which only ever runs from the default branch:
name: Accessibility baseline
on:
push:
branches: [main]
jobs:
a11y-baseline:
runs-on: ubuntu-latest
steps:
- uses: WCAG-Compliance/wcagc-ci@v1
with:
api-key: ${{ secrets.WCAGC_API_KEY }}
urls: |
https://staging.example.com/
https://staging.example.com/signup
https://staging.example.com/checkout
set-baseline: "true"
fail-on: none
fail-on: none on the baseline job matters. You want main to record its state even when that state is bad, because the point is to compare against reality, not to block the push that already happened.
The fail-on input takes four values: new-critical (the default), any-critical, serious-or-worse, and none. Start with new-critical. Once the critical backlog is gone, tighten it.
How do I make the check block the merge?
A failing job on its own doesn't stop anyone. You have to tell GitHub the job is required.
In the repository settings, add a branch protection rule for main, turn on "Require status checks to pass before merging", and search for the job. With the workflow above it shows up as a11y-gate. If it isn't in the search yet, open a throwaway pull request so the job runs once. Rulesets have the same rule ("Require status checks to pass before merging"), and unlike classic protection, more than one ruleset can apply to a branch at a time (GitHub Docs).
There is one more setting worth a deliberate choice. "Require branches to be up to date before merging" is GitHub's strict mode: the branch has to include the latest base commit before it can merge (GitHub Docs). For an accessibility gate I'd leave it off on busy repos. Each forced rebase re-runs the check and spends another scan, and accessibility regressions rarely come from two pull requests that are each fine on their own.
My opinion, for what it's worth: let the gate fail closed. The Action exits 1 not only on a FAIL verdict but also on an API error or a wait timeout (wcagc-ci README). Some teams find that annoying and wrap it in continue-on-error: true. Don't. If the scanner is down for twenty minutes, an admin can bypass the rule for one urgent merge. A gate that quietly passes when it can't see anything is worse than no gate, because people stop reading it.
How does the baseline decide what counts as "new"?
This is the part I'd read twice before trusting the gate.
When a check starts, wcagc picks the most recent completed check on that site that was saved with set-baseline: "true". Each finding gets a key built from three things: the page URL, the rule ID, and the CSS selector of the failing element. A finding in the pull request whose key isn't in the baseline is new. A baseline key that no longer appears counts as fixed. The PR comment and step summary show both numbers.
That design has consequences you should expect:
- A refactor can produce "new" findings. Rename
.btn-primaryto.button--primaryand an old contrast failure now has a different selector. The Action reports it as new, because by its key it is. The fix is the same as for a real regression: fix the contrast, or accept it into the baseline by merging and lettingmainre-baseline. - The first run has nothing to compare against. With no baseline,
new-criticalfalls back toany-criticaland says so in the step summary. Run the baseline workflow onmainonce before you make the check required. - Severity drives the verdict.
new-criticalignores a newseriousfinding. That is deliberate for a first rollout, and it's also why the counts table in the summary lists all four severities with a "New" column. Read it even on green.
Which URLs should the check run against?
The Action scans live URLs. It never sees your branch's source code, so the gate is only as honest as the deployment the URLs point at.
That gives you two workable setups. You can deploy each pull request to a stable staging host you've registered in wcagc, then run the check after the deploy step in the same workflow. Or, if your previews get a fresh hostname per branch (common on hosted preview platforms), register a fixed preview host you control and route the preview there. A random per-branch hostname won't work, since every URL in a run must belong to one verified site, and the baseline is tied to that site.
Pick URLs where your users actually get stuck: sign-up, login, checkout, the settings form. Five carefully chosen pages beat twenty marketing pages. If you need help choosing, the WCAG checklist marks which criteria an engine can check at all, which is a fair proxy for which pages a scan will say something useful about.
What happens with pull requests from forks?
On a public repo with outside contributors, the API key won't be there. GitHub's docs are plain about it: "With the exception of GITHUB_TOKEN, secrets are not passed to the runner when a workflow is triggered from a forked repository" (GitHub Docs). The step fails, and if it's required, every fork pull request is stuck.
The tempting fix is an if: that skips the job for forks. Be aware of what that means: a skipped required check counts as passing (GitHub Docs). Fork pull requests would merge with no accessibility check at all. On repos where that matters, I'd keep the failure and have a maintainer run the check from a branch in the main repository before merging. The optional PR comment follows the same rule: on a fork pull request, or without pull-requests: write, it writes a skip note to the step summary and never changes the exit code.
What will a green accessibility check not tell you?
W3C puts it in one line: "Web accessibility evaluation tools can not determine accessibility, they can only assist in doing so" (W3C WAI). The gate will tell you an image has no alt attribute. It won't tell you that alt="hero-final-v3.png" is useless. It flags a button without an accessible name, but not a focus order that jumps from the header to the footer. Keyboard traps in a custom date picker, error messages that make sense only if you can see the red border, captions that are out of sync: those need a person with a keyboard and a screen reader.
That matters beyond code quality. In the EU, EN 301 549 is the standard the European Accessibility Act leans on. In the US, the DOJ's ADA Title II rule adopts WCAG 2.1 Level AA (ADA.gov), and the Section 508 standards incorporate WCAG 2.0 Level AA (U.S. Access Board). All of them point at success criteria, not at the output of a scanner. A CI gate is evidence that you keep machine-detectable regressions out. It is not evidence of conformance, and the Action's own step summary says so on every run.
Frequently asked questions
Can a GitHub Action block a pull request on accessibility issues?
Yes, if the job is a required status check. The Action exits with code 1 on a failing verdict, and branch protection or a ruleset keeps the pull request from merging until the check passes or an admin bypasses the rule.
Should the accessibility gate fail on all issues or only new ones?
Only new ones, at least at the start. A baseline saved from main lets the check fail on what the pull request introduced, so the existing backlog becomes planned work instead of a permanently red build.
Why did my pull request show "new" findings I didn't cause?
Usually because a selector changed. wcagc matches findings by page URL, rule ID and CSS selector, so renaming a class or restructuring markup turns an old finding into a new key. Fix it in the pull request or let main re-baseline after merge.
Does a passing accessibility GitHub Action mean my site is WCAG compliant?
No. Automated tools covered 57% of issues by volume in Deque's 2021 study, and W3C says tools can only assist in determining accessibility. A green check means no new machine-detectable regressions, and full conformance needs manual review.
How do I handle pull requests from forks?
Expect the check to fail, because GitHub doesn't pass repository secrets to workflows triggered from forks. Skipping the job makes the required check count as passing, so on repos that care, run the check from a branch in the main repository before merging.
Try it on one page first
Before you make anything required, see what the engine says about a page you care about. Run a free scan on your sign-up or checkout page. If the results look like something you'd want in every pull request, the GitHub integration guide has the workflow and the API key steps, and you can create a free account to get the key.
Pavel Charkasau, founder, wcagc.com. Last updated 6 October 2026.
Sources
- About protected branches, GitHub Docs — required checks must be successful, skipped or neutral; strict vs loose checks; rulesets can stack. Accessed 6 October 2026.
- Available rules for rulesets, GitHub Docs — "Require status checks to pass before merging" in rulesets. Accessed 6 October 2026.
- Using secrets in GitHub Actions, GitHub Docs — secrets other than
GITHUB_TOKENare not passed to workflows triggered from forks. Accessed 6 October 2026. - WCAG-Compliance/wcagc-ci, GitHub — Action inputs,
fail-onpolicies, baseline fallback, exit codes, fork comment behaviour. Accessed 6 October 2026. - Automated testing identifies 57% of digital accessibility issues, Deque — 2,000+ audits, 13,000+ pages, nearly 300,000 issues (10 March 2021). Accessed 6 October 2026.
- Fact Sheet: New Rule on the Accessibility of Web Content and Mobile Apps, ADA.gov — Title II rule adopts WCAG 2.1 Level AA. Accessed 6 October 2026.
- ICT Accessibility 508 Standards and 255 Guidelines, U.S. Access Board — Section 508 incorporates WCAG 2.0 Level AA by reference. Accessed 6 October 2026.
- Selecting Web Accessibility Evaluation Tools, W3C WAI — tools cannot determine accessibility, they can only assist (updated 13 May 2024). Accessed 6 October 2026.