
Research
/Security News
737 Chrome VPN Extensions Linked to Brand Impersonation and Browser Traffic Redirection
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.
Makes AI coding agents write accessible UI by default, then proves what it can prove with a real browser audit across the states they built — and tells you exactly what it could not check.
[!IMPORTANT]
🤖 Read this with your AI agent — don't read it by hand.
This repo is written agent-first. Point Claude Code, GitHub Copilot, Cursor, or any agent at it: "Read the README and AGENTS.md, then help me run / extend this." Structure +
AGENTS.mdare optimized for agent comprehension.
a11y-loop makes AI coding agents write accessible UI by default, then proves what it can prove with a real browser audit across the states it built — and tells you exactly what it could not check.
Demo GIF · SKILL.md · Benchmark · Changelog · Report Bug · Request Feature
AI coding agents write inaccessible UI by default — 84% of AI-generated web pages carry accessibility issues (W4A'24), and telling the model to "be accessible" barely moves that number: accessibility-oriented prompts have measured a slightly higher violation rate than accessibility-agnostic ones (W4A'25, 17.32% vs. 15.93%). Instructions alone are not the fix.
a11y-loop is two things working together:
a11y-loop audit / contrast --fix / diff) that verifies the result in a
real browser, feeds failures back to the agent to fix, and re-audits until the loop converges.The skill follows the open Agent Skills standard, so the
same SKILL.md is portable across 40+ clients — Claude Code, Cursor, GitHub Copilot, Codex,
Gemini CLI, and more — not just one vendor's agent. The CLI runs the same checks standalone in CI
or any other pipeline.
a11y-loop is a personal open-source project. It exists because the same handful of failures — low contrast, missing alt text, empty links and buttons, unlabelled form inputs — have topped the WebAIM Million every year for a decade, and the reason is not that they are hard to fix. It is that accessibility usually arrives as an after-the-fact compliance check, once the UI is already built and nobody wants to touch it. The goal here is to move it earlier: make accessibility a default of the development workflow rather than an audit at the end — which matters more now that so much UI code is written by AI agents in the first place.
What this is not. a11y-loop does not claim compliance, does not guarantee accessibility, and does not replace manual testing or testing with assistive technology. A clean report means "no automatically detectable failures" — see Honest Coverage below for exactly what that does and doesn't cover.

The demo page (demo/before/) is a fictional conference site seeded with
163 detectable failures spanning the WebAIM Million's top six failure classes plus the
failure modes documented for LLM-generated UI: clickable divs, hidden focus indicators, skipped
heading levels, broken ARIA references, and ignored prefers-reduced-motion. Running the loop
against it converges on demo/after/ — 0 violations — and
a11y-loop diff --before demo/before/report.json --after demo/after/report.json reports:
Converged: all 163 violations fixed, none introduced
FIXED / NEW / REMAINING status per finding, matched by stable fingerprint (rule + selector +
WCAG success criterion), is what makes that verdict checkable rather than asserted — see
demo/before/VIOLATIONS.md and
demo/after/FIXES.md for the finding-by-finding record.
1 Five rendering passes per audit — default (1280×720), dark mode, forced-colors mode,
reduced-motion, and a 320×256 reflow viewport (the WCAG-sanctioned 400% zoom equivalent for
SC 1.4.10) — because most real failures only show up under a specific rendering condition, not on
a single default-viewport load.
2 Checks axe-core can't run — tab order, focus visibility (including focus-ring contrast),
dialog focus trap / Escape / focus-return, target size (24×24 CSS px, SC 2.5.8), reduced-motion
effectiveness, ambiguous link text, div-as-button, and positive tabindex. These sit alongside
axe-core, not instead of it.
3 State coverage, not just page-load — --interact drives the states an agent just built
(a modal opened, a menu expanded, a form in its error state) through the same five passes, because
axe running once on page load finds nothing in a dialog that only misbehaves once it's open.
4 Stable fingerprints power regression detection — a11y-loop diff matches findings across
two reports by rule + selector + WCAG success criterion and classifies each as FIXED, NEW
(a regression — this fails the command even if the total count went down), or REMAINING.
5 Structured, honest output — JSON is the primary format: every finding carries its WCAG
success criterion, ACT rule ID where one exists, and a provenance block (axe-core version, browser,
URL, viewport, timestamp, states exercised). SARIF v2.1 is available as a secondary format with
a documented limitation rather than a silent gap. A generated manual-review
checklist, scoped to what was actually built, ships with every run.
6 Exit codes are the loop's contract — 0 no violations / threshold met / no regression,
1 violations found / regression introduced, 2 tool error — so a CI pipeline or an agent's own
control flow can branch on the result without parsing prose.
7 contrast --fix — checks a foreground/background pair against WCAG 2.x (1.4.3: 4.5:1
normal text / 3:1 large text; 1.4.11: 3:1 for UI components) and, on failure, suggests passing
colors in both directions (lighter and darker) in OKLCh, so a fix stays close to the original hue
instead of jumping to black or white.
8 Portable as an Agent Skill — plain SKILL.md + references/, no proprietary format, works
in any client implementing the open Agent Skills standard, not only Claude Code.
Automated accessibility testing has a real, bounded scope, and a11y-loop says so in every report rather than implying otherwise:
incomplete results are surfaced as needsReview, not suppressed. See
evals/benchmark-results.md for a live example of two such findings being investigated and
resolved rather than dismissed.A small, illustrative comparison in evals/benchmark-results.md:
the same six UI components, built by the same model (Claude Sonnet 5, as a Claude Code subagent),
once with no accessibility guidance and once following the skill and running the audit loop to
convergence.
Read this with its stated caveats: N = 6 components, a single run per condition, one model family, and the audits are produced by a11y-loop's own engine (mitigated, not eliminated, by every finding being grounded in axe-core, a third-party rules engine) — a small, self-audited illustration of the effect's shape, not a controlled study or a precise effect size.
"type": "module")@axe-core/playwright (MPL-2.0 — see THIRD-PARTY-NOTICES.md)SKILL.md + references/, no client-proprietary formatnode --test runner, 337+ tests across unit and integration suites,
including an 18-fixture seeded-violation matrix, a demo end-to-end run, and a forced-colors
gradient regression testgraph TD
A["Agent Skill<br/>skill/a11y-loop/SKILL.md + references/"] -->|standing generation rules| B["Agent writes UI code<br/>HTML / JSX / Vue / Svelte / Astro / CSS"]
B --> C["a11y-loop CLI"]
C --> D["audit<br/>5 passes: default, dark,<br/>forced-colors, reduced-motion, 320px reflow"]
C --> E["contrast --fix<br/>WCAG 2.x + OKLCh suggestions"]
C --> F["diff<br/>FIXED / NEW / REMAINING"]
D --> G["Playwright + Chromium"]
G --> H["axe-core"]
G --> I["a11y-loop's own checks<br/>focus, dialog trap, target size,<br/>reduced motion, link text, div-button"]
D -->|--interact| J["Drive built states:<br/>modal open, menu expanded, form error"]
H --> K["JSON report<br/>WCAG SC + ACT IDs + provenance"]
I --> K
J --> K
K --> L["SARIF v2.1<br/>(secondary format)"]
K --> M["Manual-review checklist"]
K -->|violations found| B
F --> K
The loop, in words: the skill sets standing rules while the agent writes UI; a11y-loop audit
verifies the rendered result across five passes plus any built interaction states; violations feed
back to the agent to fix; a11y-loop diff confirms convergence without new regressions; the JSON
report (and its manual-review checklist) is the artifact of record, with SARIF offered as a
secondary format for tools that consume it.
Not yet published to npm and this repo has no GitHub remote yet — install from a local clone for now:
# Clone and install
git clone https://github.com/ChanMeng666/a11y-loop.git
cd a11y-loop
npm install
# Install the Chromium build Playwright needs for audits
npx playwright install chromium
Once published, the intended install is npx a11y-loop <command> with no local setup at all —
that comes after the first npm publish.
If you keep browser binaries off the system drive, set PLAYWRIGHT_BROWSERS_PATH before running
npx playwright install chromium (and before running the test suite, which launches the same
browser) — e.g. PLAYWRIGHT_BROWSERS_PATH=D:\playwright-browsers.
Copy the skill directory into any Agent Skills-compatible client:
# Personal, all projects (Claude Code and other clients that read ~/.claude/skills)
cp -r skill/a11y-loop ~/.claude/skills/a11y-loop
# Or project-scoped
cp -r skill/a11y-loop .claude/skills/a11y-loop
Any client implementing the Agent Skills specification can load it the same way — Claude Code, Cursor, GitHub Copilot, Codex, Gemini CLI, and more.
a11y-loop audit http://localhost:3000 --json --out report.json
exits 1 if any violation is found (2 on a tool error such as a missing browser), so a build
step can gate on it directly:
a11y-loop audit http://localhost:3000 --json --out report.json || exit 1
a11y-loop diff --before base.json --after head.json in a PR check turns that into a regression
gate: it fails only on genuinely NEW violations, so a PR that fixes ten and introduces none
passes even though the raw count changed.
# Audit a running page (five passes: default, dark, forced-colors, reduced-motion, reflow)
a11y-loop audit http://localhost:3000
# Audit an HTML file (served locally, never over file://)
a11y-loop audit --file ./dist/index.html
# Audit an HTML fragment directly — the usual entry point for an agent mid-generation
a11y-loop audit --html "<button class=\"icon-btn\"><svg .../></button>"
# Audit an interactive state (e.g. a modal after it opens)
a11y-loop audit http://localhost:3000 --interact ./states/modal-open.mjs
# Check a color pair against WCAG 2.x and get OKLCh fix suggestions
a11y-loop contrast "#767676" "#ffffff" --fix
# Compare two audit reports for regressions
a11y-loop diff --before base.json --after head.json
| Flag | Applies to | Effect |
|---|---|---|
--json | audit, contrast | machine-readable output on stdout |
--out <path> | audit | write the JSON report to a file |
--sarif <path> | audit | also write SARIF v2.1 (see Honest Coverage) |
--interact <path.mjs> | audit | export const states = { name: async (page) => {} } to drive built states |
--headed | audit | run a visible browser, for debugging |
--no-best-practice | audit | omit axe best-practice rules (never blocking either way) |
--quiet | audit | one-line summary only |
--large | contrast | large-scale text thresholds (≥24px, or ≥18.5px bold) |
--ui | contrast | non-text / UI component threshold, 3:1, SC 1.4.11 |
--fix | contrast | suggest passing colors, lighter and darker, in OKLCh |
Run a11y-loop --help for the full, current reference.
npm install
npx playwright install chromium # once, or after a Playwright version bump
npm test # full suite
npm run test:unit # unit tests only
npm run test:integration # integration tests only (drives real Chromium)
See AGENTS.md for AI-agent-oriented project conventions, the fixture-manifest
testing pattern, and the loop discipline expected when touching demo/ or other UI.
Contributions make the open-source community an amazing place to learn and create. Please read the Contributing Guide and the Code of Conduct before you start, and use the provided issue / pull-request templates.
If this project helps you, please consider supporting its development:
For questions and help, see SUPPORT.md. For security issues, see SECURITY.md.
This project is released under the MIT license. axe-core, a dependency this project
uses to run its checks, is separately licensed under MPL-2.0 — see
THIRD-PARTY-NOTICES.md for full attribution.
Chan Meng
FAQs
Makes AI coding agents decide accessibility while the work is still being planned and write accessible UI by default, then proves what it can prove with a real browser audit across the states they built — and tells you exactly what it could not check.
The npm package a11y-loop receives a total of 57 weekly downloads. As such, a11y-loop popularity was classified as not popular.
We found that a11y-loop demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.