What If Your A11y Linter Could Actually Fix the Bugs It Found?

Published: (February 15, 2026 at 10:54 AM EST)
5 min read
Source: Dev.to

Source: Dev.to

GitHub Copilot CLI Challenge submission

What if your linter could actually fix the problems it found?

a11y‑pilot is a CLI that scans your frontend code for accessibility violations, then spawns GitHub Copilot CLI to fix each one.
It doesn’t just suggest a change – it invokes copilot --prompt with a crafted fix instruction and lets the AI refactor your code in place.

  • npm:
  • GitHub:

How it works

Scan → Detect → Prompt Engineer → Copilot CLI Fixes → Done
StepWhat happens
ScanPoint the tool at any directory or file. It walks the project and parses JSX, TSX, HTML, Vue, Svelte, and Astro files using Babel AST (for JSX/TSX) and htmlparser2 (for HTML‑like templates).
Detect15 accessibility rules run against every parsed element, checking for WCAG violations across 5 categories.
Auto‑fixFor each issue, a11y‑pilot builds a precise, context‑rich prompt and runs copilot --prompt --allow-all-tools. Copilot reads the file, understands the surrounding code, and applies the minimum diff needed – no blind find‑and‑replace.

Under‑the‑hood diagram

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   a11y‑pilot    │────▶│  Issue detected   │────▶│  Build prompt   │
│   scanner       │     │  (rule engine)    │     │  (context‑rich) │
└─────────────────┘     └──────────────────┘     └────────┬────────┘


┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   File fixed!   │◀────│  Copilot applies │◀────│  copilot CLI    │
│   ✔ Report      │     │  the fix         │     │  invoked        │
└─────────────────┘     └──────────────────┘     └─────────────────┘

Example prompt (no-div-button rule)

In file src/Hero.tsx at line 20, fix this accessibility issue:
has a click handler but is missing `role` and `tabIndex`. **Fix:** Replace the element with a native “ element, move the onClick handler to the button, and remove any cursor: pointer styling (buttons have it by default).
Only modify the minimum code necessary.

Copilot CLI reads the full file context, understands the surrounding JSX structure, and makes an intelligent refactor – not a simple string replace.

Terminal dashboard (after a scan)

✖ Found 46 issues (35 errors, 11 warnings) in 4 files (5 scanned)

📊 Issue Breakdown
──────────────────────────────────────────────────
 ♿ Accessibility       ████████░░░░░░░░░░░░   19 (41%)  19E
 🏗️ Semantic HTML      █████░░░░░░░░░░░░░░░   11 (24%)   4E 7W
 ⌨️ Keyboard            ███░░░░░░░░░░░░░░░░░    7 (15%)   5E 2W
 🏷️ ARIA               ██░░░░░░░░░░░░░░░░░░    5 (11%)   5E
 👆 Interaction         ██░░░░░░░░░░░░░░░░░░    4 (9%)    2E 2W
──────────────────────────────────────────────────
14 rules triggered: img‑alt, form‑label, no‑div‑button, …

The dashboard gives an instant snapshot of where your accessibility debt lives – ARIA misuse, keyboard traps, missing semantics, etc. – so you know exactly where to focus.

Copilot CLI – the execution engine

  1. --auto-fix – When triggered, a11y‑pilot spawns

    copilot --prompt "" --allow-all-tools

    for each detected issue.
    --prompt enables non‑interactive mode, and --allow-all-tools auto‑approves tool use, making programmatic invocation possible.

  2. Quality of fixes

    • alert('clicked')}>converted to “ with the handler moved and className preserved.
    • Empty [](/profile)added aria-label="Profile" (label inferred from URL).
  3. Prompt engineering per rule – Each of the 15 rules carries a copilotPrompt field: a concise instruction that gives Copilot enough context to understand the problem and the expected fix pattern, while still allowing adaptation to surrounding code. This is what turns a11y‑pilot from a “linter” into a “linter + AI‑powered fixer”.

Development workflow with Copilot CLI

AreaHow Copilot helped
Parser logicIterated on Babel AST traversal for JSX elements, handling spread props, expression containers, and member expressions. Copilot clarified the @babel/traverse ESM default‑export quirk (`_traverse.default
Rule implementationReferenced WCAG criteria, ensured edge‑case handling (e.g., not flagging “ without href in the aria-hidden-focus rule, skipping input[type="hidden"] in form‑label).
DebuggingQuickly reproduced and fixed parsing bugs, generated test fixtures, and wrote unit tests for each rule.
Prompt refinementRe‑crafted prompts until the AI produced minimal, correct diffs for a wide variety of codebases.

Real‑world result

I pointed a11y‑pilot at a file with 12 accessibility issues and ran a11y‑pilot fix.
Every single issue was resolvedbecame, empty “ got meaningful alt text inferred from the filename, unlabeled inputs received proper aria-label.
Re‑scanning the file: 0 issues. No manual edits required.

Try it yourself

# Install globally (or as a dev dependency)
npm i -g a11y-pilot

# Scan a project
a11y-pilot scan ./src

# Auto‑fix everything
a11y-pilot fix ./src

Enjoy an accessibility‑first workflow powered by GitHub Copilot CLI!

When the copilot‑bridge initially used shell: true it triggered the Node.js DEP0190 deprecation warning.
Copilot CLI helped me switch to direct binary resolution with:

execFileSync('which', ['copilot'])

and use a proper spawn() call without a shell.

What impressed me

The thing that stood out most was Copilot CLI’s ability to handle file‑level context.
When I pointed it at a file with 12 accessibility issues and said:

“Fix the “ on line 20”

it didn’t break the other 11 problematic lines. It made surgical, minimal edits.
That property made the auto‑fix feature viable—I could confidently fix issues one‑by‑one or in batches without worrying about cascading breakage.

Install

npm install -g a11y-pilot

Repository

GitHub: safvan-tsy/a11y-pilot

Try it now

npx a11y-pilot scan ./src
0 views
Back to Blog

Related posts

Read more »