← All Sparks

Sparks, Tools

a11y-ci catches the accessibility issues a pull request adds

Most teams have an accessibility backlog. Fail the build on all of it and somebody turns the check off. a11y-ci only flags what your change adds.

By Mike Binder2 min read

a11y-ci reports only what a pull request addsLeft, the base branch has seven existing accessibility issues, the backlog. Middle, the pull request has the same seven plus two new ones: a button with no name and an image with no alt text. Right, the a11y-ci pull request comment lists only the two new issues, ranked by impact, and notes that seven existing issues were not counted against the change.Base branchThe backlog you already have7 existing issuesThis pull requestSame backlog, two new problems+Button has no name+Image has no alt textPR commentOnly what this change addeda11y-ci2 new issues1 CRITICALButton has no nameA screen reader user hears "button"2 SERIOUSImage has no alt textWCAG 1.1.17 existing issues not countedAdvisory. Blocking is opt-in.

Every team I have worked with has an accessibility backlog. Hundreds of old issues nobody has time for this sprint.

Here is the problem. Add an accessibility check to your build that fails on all of those old issues, and it fails on every pull request. Within a week somebody turns it off. I have watched it happen more than once.

I wanted a check that asks one question: did this change make things worse?

That is a11y-ci. It is free on GitHub at InnerSpark/a11y-ci.

How it works

It loads your app in a real browser, the way a user would see it. Then it does the same for your main branch and compares the two. You only hear about the problems your change added.

Only new problems block you.

It also sorts what it finds by who it hurts. “A blind user cannot submit this form” shows up before “the page title is a little long.”

Two places it catches things

While you write the code. Run a11y-ci lint and it reads your HTML and JSX for the classic mistakes: a clickable <div>, an image with no alt text, a button with no name. It takes about half a second, so you can run it every time you commit.

On the pull request. The GitHub Action checks your preview build against main and leaves a comment with anything new. It starts out as a heads-up only. Once you trust it, you can let it block merges.

- uses: InnerSpark/a11y-ci/[email protected]
  with:
    base-url: ${{ env.PREVIEW_BASE_URL }}
    head-url: ${{ env.PREVIEW_HEAD_URL }}
    routes: "/,/checkout,/account"
    fail-on: none   # set to 'new-serious' to block

Everything runs in your own pipeline. Nothing gets sent anywhere.

Where AI fits in

If you want, plug in your own Claude key. It will suggest a fix for each new issue, and look at things a scanner can’t judge, like alt text that is there but says nothing useful.

The AI only gives advice. It never decides whether your build passes. A check that can block your merge needs to give the same answer every time.

What it misses

I would rather tell you up front.

  • It takes one snapshot of the page. Anything that happens later, like an auto-logout or a carousel that moves on its own, it never sees.
  • It can’t tell how important a button is. It will flag a new icon button with no label. You still have to decide if that button is “Add to cart” or something minor.
  • Look-alikes can slip by. If two unlabeled buttons look the same to it and swap places, it won’t notice.

Automated tools catch part of the picture. Somebody with a keyboard and a screen reader still needs to check the rest.

Try it

npx @a11yci/cli lint src

Want dashboards, full-site crawls, pages behind a login, and VPAT reports? That is AccessSpark.

Keep reading

  • Accessibility

    Five AI tools, one keyboard, and the fix

Work with meAccessibility and design systems consulting