icansee: accessibility gate and reviewer
This skill installs and operates a three-layer WCAG 2.1 A/AA gate that
blocks accessibility regressions before they ship. Same rule set as the
Vercel toolbar's Accessibility Audit Tool (axe-core).
It also serves as an on-demand reviewer for contrast questions, palette
audits, and snippet reviews when the user isn't asking for a full
install.
When to use
- "set up / install a11y / accessibility checks in this repo"
- "block commits with accessibility issues"
- "configure WCAG / axe-core compliance"
- "review this for a11y / WCAG"
- "does color X on Y pass AA / AAA"
- "audit my design tokens / palette"
- The user is in a project that already has
.icansee/. The skill is
active there. Keep edits consistent with its conventions.
Architecture (so you can answer questions about it)
Three layers, complementary:
| Layer |
Trigger |
Engine |
Catches |
Speed |
| Pre-commit |
git commit |
audit.sh → eslint plugins + html_audit.py + palette_audit.py |
Source-level rule violations on staged files: missing alt, no label, bad ARIA, palette failures. |
~1–3s |
| Pre-push |
git push |
rendered_audit.sh → Playwright + @axe-core/playwright against built+served site, sweeping configured color modes |
Rendered-DOM rules that source can't see: computed contrast through CSS cascade, landmark coverage, focus state, dark-mode contrast via prefers-color-scheme emulation. |
~30–90s |
| CI |
PR / push |
Same as pre-push, in GitHub Actions (.github/workflows/a11y.yml) |
Same as pre-push, but enforced at the merge boundary where --no-verify cannot help. |
~1–5min |
Why three layers, not two:
- The pre-commit gate is fast enough to run on every commit. It's
static-only by design, which keeps each-commit feedback under a few
seconds.
- The pre-push gate closes the rendered-DOM gap locally. Same engine as
the Vercel toolbar's Accessibility Audit Tool. It runs once per push,
not per commit, so wall-clock cost is amortized.
- CI is the ungame-able boundary.
git push --no-verify skips pre-push;
CI doesn't honor it. Required because contributors will --no-verify
at some point.
All three block on any issue. Bypasses are deliberate escape hatches:
git commit --no-verify for pre-commit, git push --no-verify for
pre-push, no bypass for CI.
Capability table
| Need |
How |
| Install the gate in a repo |
Run scripts/install.sh from the repo root. |
| Detect what frameworks are present |
scripts/install.sh --print-detection |
| Lint staged files manually |
scripts/audit.sh --staged |
| Lint everything in the repo |
scripts/audit.sh --all |
| Lint specific files |
scripts/audit.sh path/to/file.tsx |
| Run rendered-DOM audit locally |
scripts/rendered_audit.sh (builds, serves, runs Playwright + axe per route × mode) |
| Check one contrast pair |
scripts/contrast.py check <fg> <bg> |
| Suggest a passing color |
scripts/contrast.py suggest <fg> <bg> --target 4.5 |
| Audit a palette / token file |
scripts/palette_audit.py matrix tokens.json |
| Static HTML a11y check |
scripts/html_audit.py page.html |
| Threshold lookup |
docs/reference/wcag-thresholds.md |
| Per-rule check guidance |
docs/reference/static-rules.md |
| Code-level fixes |
docs/reference/remediation-patterns.md |
| Install flow / CI translation / troubleshooting |
docs/reference/install-and-ci.md |
Workflow: three common shapes
A. Install or configure the gate
When the user asks to set this up in their project:
- Confirm the cwd is a git repo. If not, offer to run
git init first.
- Run
scripts/install.sh and surface its output. The installer
detects frameworks from package.json and filesystem hints, installs
the right ESLint plugin(s) via the project's package manager
(npm/pnpm/yarn/bun, picked from lockfile), drops flat-config files
into .icansee/, wires the pre-commit hook into .husky/ (if husky
is present) or .git/hooks/pre-commit, and copies
.github/workflows/a11y.yml along with .icansee/routes.json.
- Walk the user through
.icansee/routes.json. List the routes that
exercise the UI surface that matters. Default is ["/"] (light mode
only). To sweep dark mode too, use the v0.3 object form:
{"routes": ["/"], "modes": ["light", "dark"]}.
- Suggest a smoke test: stage an intentional violation and try to
commit.
- If the user is not on GitHub Actions, point them at
docs/reference/install-and-ci.md for translations to GitLab,
CircleCI, Bitbucket, or Vercel.
B. The gate fired on a commit and the user asks for help
The pre-commit output prints file:line:col [impact] rule: message per
finding. For each:
- Identify the rule by matching against
docs/reference/static-rules.md.
- Read the offending file at the indicated line.
- Apply the canonical fix from
docs/reference/remediation-patterns.md or tailor to the user's
stack.
- Re-stage and re-run
scripts/audit.sh --staged to verify before
suggesting they commit.
- Never recommend
git commit --no-verify as a fix. It's an escape
hatch only, and only if the user explicitly chooses it.
C. Ad-hoc review (no install requested)
Use the on-demand scripts directly. Three sub-shapes:
- "Does color X on Y pass?" Call
scripts/contrast.py check. Report
the exact ratio plus pass/fail at AA normal, AA large, AAA normal, AAA
large, and 1.4.11 non-text. If failing, run scripts/contrast.py suggest.
- "Audit this palette / token file." Run
scripts/palette_audit.py matrix. Group by failing pair count; surface suggested replacements
for each.
- "Review this component / page." Read source. Walk the categories in
docs/reference/static-rules.md. For every text element with
resolvable foreground/background, run scripts/contrast.py. Group
findings by severity. Anything needing a rendered DOM goes in the
"manual review" bucket. Recommend the CI path or running the Vercel
toolbar locally.
Reporting findings
Lead with the count, like 3 must-fix, 2 should-fix, 1 needs manual review. Each finding includes:
- Where:
file_path:line_number.
- Rule: axe-core rule id and WCAG SC (e.g.,
color-contrast, 1.4.3 AA).
- What's wrong: one sentence.
- Fix: concrete code or color change. Pull from
docs/reference/remediation-patterns.md when it fits.
- For contrast: ratio achieved, threshold, suggested color.
Constraints
- Use the scripts; don't estimate. Contrast is exact math. Reported
ratios must match what axe-core, DevTools, and WebAIM produce.
- Never round near a threshold. 4.49:1 fails AA. Say so.
- Disabled controls (
disabled attr or aria-disabled="true" with
enforced behavior) are exempt from 1.4.3 and 1.4.6.
- Don't recommend ARIA where native HTML works. A
<button> already has
role="button"; adding it is noise.
- Don't auto-fix without asking. Report findings, propose fixes, let the
user pick which to apply.
- Pre-commit alone is not parity with Vercel's toolbar. It's static.
The pre-push and CI layers are the parity layers (same axe-core
engine). Be honest about this when the user asks what each layer
catches.
Pointers
scripts/install.sh: project installer, idempotent. Default installs
all three layers; --no-pre-push, --no-ci, --no-hook opt out
individually.
scripts/audit.sh: pre-commit entrypoint and CLI runner.
scripts/rendered_audit.sh: pre-push entrypoint. Build, serve, hand
off to .icansee/axe-runner.mjs (Playwright + @axe-core/playwright).
Honors BUILD_CMD, SERVE_CMD, BASE_URL env or .icansee/env file.
templates/axe-runner.mjs: the per-route × per-mode axe runner. Copied
to .icansee/axe-runner.mjs at install time.
scripts/contrast.py: exact WCAG ratio plus suggest.
scripts/palette_audit.py: token matrix audit.
scripts/html_audit.py: stdlib static HTML a11y check.
templates/eslint/*.config.mjs: flat-config rule sets per framework.
templates/pre-commit: git pre-commit hook script.
templates/pre-push: git pre-push hook script (skips when only docs
change).
templates/github-workflow-a11y.yml: CI workflow that runs the same
Playwright + axe runner.
docs/: full human-readable documentation, organized via the
Diátaxis framework. Start at
docs/README.md. Sections:
docs/tutorials/: guided walkthroughs (start with
getting-started.md).
docs/how-to/: task recipes (install, routes, auth, slow builds,
non-GitHub CI, debug a blocked commit, opt-in/out of layers).
docs/reference/: authoritative lookups (scripts, env vars, exit
codes, ESLint configs, WCAG thresholds, static rules, remediation
patterns, install-and-ci).
docs/explanation/: design rationale (three-layer architecture,
parity with Vercel, block vs warn, why axe-core).
- Spec: https://www.w3.org/TR/WCAG22/
- axe-core rules: https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
- Vercel parity: https://vercel.com/docs/vercel-toolbar/accessibility-audit-tool
1---2name: icansee3description: Install and operate a three-layer WCAG 2.1 A/AA accessibility gate that blocks commits and pushes with a11y issues. Same axe-core rule set as Vercel's Accessibility Audit Tool. Layers are (1) a pre-commit hook running static checks (jsx-a11y, vuejs-accessibility, svelte, angular-template, astro, plain HTML, palette tokens) on staged files, (2) a pre-push hook running Playwright + @axe-core/playwright against the built and served site to catch rendered-DOM rules (computed contrast, landmark coverage, focus state) and to sweep light/dark color modes via prefers-color-scheme emulation, and (3) a GitHub Actions workflow enforcing the same rendered audit at the PR boundary. Also handles on-demand reviews like contrast ratio checks, palette audits, and snippet reviews. Use when the user asks to set up a11y CI, block accessibility issues at commit, audit a palette, check a contrast ratio, or review code for WCAG compliance.4---56# icansee: accessibility gate and reviewer78This skill installs and operates a three-layer WCAG 2.1 A/AA gate that9blocks accessibility regressions before they ship. Same rule set as the10Vercel toolbar's Accessibility Audit Tool (axe-core).1112It also serves as an on-demand reviewer for contrast questions, palette13audits, and snippet reviews when the user isn't asking for a full14install.1516## When to use1718- "set up / install a11y / accessibility checks in this repo"19- "block commits with accessibility issues"20- "configure WCAG / axe-core compliance"21- "review this for a11y / WCAG"22- "does color X on Y pass AA / AAA"23- "audit my design tokens / palette"24- The user is in a project that already has `.icansee/`. The skill is25 active there. Keep edits consistent with its conventions.2627## Architecture (so you can answer questions about it)2829Three layers, complementary:3031| Layer | Trigger | Engine | Catches | Speed |32| ------------ | -------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | --------- |33| Pre-commit | `git commit` | `audit.sh` → eslint plugins + `html_audit.py` + `palette_audit.py` | Source-level rule violations on staged files: missing alt, no label, bad ARIA, palette failures. | ~1–3s |34| Pre-push | `git push` | `rendered_audit.sh` → Playwright + `@axe-core/playwright` against built+served site, sweeping configured color modes | Rendered-DOM rules that source can't see: computed contrast through CSS cascade, landmark coverage, focus state, dark-mode contrast via `prefers-color-scheme` emulation. | ~30–90s |35| CI | PR / push | Same as pre-push, in GitHub Actions (`.github/workflows/a11y.yml`) | Same as pre-push, but enforced at the merge boundary where `--no-verify` cannot help. | ~1–5min |3637Why three layers, not two:3839- The pre-commit gate is fast enough to run on every commit. It's40 static-only by design, which keeps each-commit feedback under a few41 seconds.42- The pre-push gate closes the rendered-DOM gap locally. Same engine as43 the Vercel toolbar's Accessibility Audit Tool. It runs once per push,44 not per commit, so wall-clock cost is amortized.45- CI is the ungame-able boundary. `git push --no-verify` skips pre-push;46 CI doesn't honor it. Required because contributors *will* `--no-verify`47 at some point.4849All three block on any issue. Bypasses are deliberate escape hatches:50`git commit --no-verify` for pre-commit, `git push --no-verify` for51pre-push, no bypass for CI.5253## Capability table5455| Need | How |56| --------------------------------------------- | -------------------------------------------------------------------- |57| Install the gate in a repo | Run `scripts/install.sh` from the repo root. |58| Detect what frameworks are present | `scripts/install.sh --print-detection` |59| Lint staged files manually | `scripts/audit.sh --staged` |60| Lint everything in the repo | `scripts/audit.sh --all` |61| Lint specific files | `scripts/audit.sh path/to/file.tsx` |62| Run rendered-DOM audit locally | `scripts/rendered_audit.sh` (builds, serves, runs Playwright + axe per route × mode) |63| Check one contrast pair | `scripts/contrast.py check <fg> <bg>` |64| Suggest a passing color | `scripts/contrast.py suggest <fg> <bg> --target 4.5` |65| Audit a palette / token file | `scripts/palette_audit.py matrix tokens.json` |66| Static HTML a11y check | `scripts/html_audit.py page.html` |67| Threshold lookup | `docs/reference/wcag-thresholds.md` |68| Per-rule check guidance | `docs/reference/static-rules.md` |69| Code-level fixes | `docs/reference/remediation-patterns.md` |70| Install flow / CI translation / troubleshooting | `docs/reference/install-and-ci.md` |7172## Workflow: three common shapes7374### A. Install or configure the gate7576When the user asks to set this up in their project:77781. Confirm the cwd is a git repo. If not, offer to run `git init` first.792. Run `scripts/install.sh` and surface its output. The installer80 detects frameworks from package.json and filesystem hints, installs81 the right ESLint plugin(s) via the project's package manager82 (npm/pnpm/yarn/bun, picked from lockfile), drops flat-config files83 into `.icansee/`, wires the pre-commit hook into `.husky/` (if husky84 is present) or `.git/hooks/pre-commit`, and copies85 `.github/workflows/a11y.yml` along with `.icansee/routes.json`.863. Walk the user through `.icansee/routes.json`. List the routes that87 exercise the UI surface that matters. Default is `["/"]` (light mode88 only). To sweep dark mode too, use the v0.3 object form:89 `{"routes": ["/"], "modes": ["light", "dark"]}`.904. Suggest a smoke test: stage an intentional violation and try to91 commit.925. If the user is **not** on GitHub Actions, point them at93 `docs/reference/install-and-ci.md` for translations to GitLab,94 CircleCI, Bitbucket, or Vercel.9596### B. The gate fired on a commit and the user asks for help9798The pre-commit output prints `file:line:col [impact] rule: message` per99finding. For each:1001011. Identify the rule by matching against102 `docs/reference/static-rules.md`.1032. Read the offending file at the indicated line.1043. Apply the canonical fix from105 `docs/reference/remediation-patterns.md` or tailor to the user's106 stack.1074. Re-stage and re-run `scripts/audit.sh --staged` to verify before108 suggesting they commit.1095. **Never recommend `git commit --no-verify` as a fix.** It's an escape110 hatch only, and only if the user explicitly chooses it.111112### C. Ad-hoc review (no install requested)113114Use the on-demand scripts directly. Three sub-shapes:115116- "Does color X on Y pass?" Call `scripts/contrast.py check`. Report117 the exact ratio plus pass/fail at AA normal, AA large, AAA normal, AAA118 large, and 1.4.11 non-text. If failing, run `scripts/contrast.py119 suggest`.120- "Audit this palette / token file." Run `scripts/palette_audit.py121 matrix`. Group by failing pair count; surface suggested replacements122 for each.123- "Review this component / page." Read source. Walk the categories in124 `docs/reference/static-rules.md`. For every text element with125 resolvable foreground/background, run `scripts/contrast.py`. Group126 findings by severity. Anything needing a rendered DOM goes in the127 "manual review" bucket. Recommend the CI path or running the Vercel128 toolbar locally.129130## Reporting findings131132Lead with the count, like `3 must-fix, 2 should-fix, 1 needs manual133review`. Each finding includes:134135- Where: `file_path:line_number`.136- Rule: axe-core rule id and WCAG SC (e.g., `color-contrast`, 1.4.3 AA).137- What's wrong: one sentence.138- Fix: concrete code or color change. Pull from139 `docs/reference/remediation-patterns.md` when it fits.140- For contrast: ratio achieved, threshold, suggested color.141142## Constraints143144- Use the scripts; don't estimate. Contrast is exact math. Reported145 ratios must match what axe-core, DevTools, and WebAIM produce.146- Never round near a threshold. 4.49:1 fails AA. Say so.147- Disabled controls (`disabled` attr or `aria-disabled="true"` with148 enforced behavior) are exempt from 1.4.3 and 1.4.6.149- Don't recommend ARIA where native HTML works. A `<button>` already has150 `role="button"`; adding it is noise.151- Don't auto-fix without asking. Report findings, propose fixes, let the152 user pick which to apply.153- Pre-commit alone is not parity with Vercel's toolbar. It's static.154 The pre-push and CI layers are the parity layers (same axe-core155 engine). Be honest about this when the user asks what each layer156 catches.157158## Pointers159160- `scripts/install.sh`: project installer, idempotent. Default installs161 all three layers; `--no-pre-push`, `--no-ci`, `--no-hook` opt out162 individually.163- `scripts/audit.sh`: pre-commit entrypoint and CLI runner.164- `scripts/rendered_audit.sh`: pre-push entrypoint. Build, serve, hand165 off to `.icansee/axe-runner.mjs` (Playwright + `@axe-core/playwright`).166 Honors `BUILD_CMD`, `SERVE_CMD`, `BASE_URL` env or `.icansee/env` file.167- `templates/axe-runner.mjs`: the per-route × per-mode axe runner. Copied168 to `.icansee/axe-runner.mjs` at install time.169- `scripts/contrast.py`: exact WCAG ratio plus suggest.170- `scripts/palette_audit.py`: token matrix audit.171- `scripts/html_audit.py`: stdlib static HTML a11y check.172- `templates/eslint/*.config.mjs`: flat-config rule sets per framework.173- `templates/pre-commit`: git pre-commit hook script.174- `templates/pre-push`: git pre-push hook script (skips when only docs175 change).176- `templates/github-workflow-a11y.yml`: CI workflow that runs the same177 Playwright + axe runner.178- `docs/`: full human-readable documentation, organized via the179 [Diátaxis](https://diataxis.fr) framework. Start at180 [docs/README.md](docs/README.md). Sections:181 - `docs/tutorials/`: guided walkthroughs (start with182 `getting-started.md`).183 - `docs/how-to/`: task recipes (install, routes, auth, slow builds,184 non-GitHub CI, debug a blocked commit, opt-in/out of layers).185 - `docs/reference/`: authoritative lookups (scripts, env vars, exit186 codes, ESLint configs, WCAG thresholds, static rules, remediation187 patterns, install-and-ci).188 - `docs/explanation/`: design rationale (three-layer architecture,189 parity with Vercel, block vs warn, why axe-core).190- Spec: https://www.w3.org/TR/WCAG22/191- axe-core rules: https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md192- Vercel parity: https://vercel.com/docs/vercel-toolbar/accessibility-audit-tool