Best for
- Use when "audit accessibility", "check a11y", "WCAG audit", "check keyboard nav", or "test screen reader".
kensaurus/cursor-kenji/skills/audit-accessibility/SKILL.md
Automated WCAG 2.2 accessibility audit using playwright-cli to crawl every page, inject axe-core via eval, test keyboard navigation, check color contrast, ARIA labels, and heading order. Use when "audit accessibility", "check a11y", "WCAG audit", "check keyboard nav", or "test screen reader".
Decision brief
Degree of freedom: MIXED — Phases 0–1, 5 [HIGH freedom]; Phases 2–3 playwright/axe/Tab sequences [LOW freedom — run exactly]. Read protocol-browser-anti-stall before any browser step.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/kensaurus/cursor-kenji --skill "skills/audit-accessibility"Inspect the Agent Skill "audit-accessibility" from https://github.com/kensaurus/cursor-kenji/blob/28a0bd8403c950f58ed063d47a858ee3493b0038/skills/audit-accessibility/SKILL.md at commit 28a0bd8403c950f58ed063d47a858ee3493b0038. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
1. Observe — quote axe rule+selector, heading list, or Tab snapshot 2. Interpret — can a keyboard or AT user complete the task? 3. Classify — WCAG fail / axe-false-positive / manual-only / correct-as-is 4. Severity — Level A = P0; Level AA = P1; best-practice = P2
If no production URL, default to http://localhost:3000 (or detected dev server port).
Scrape 2–3 authoritative results:
For each page discovered in Phase 0a:
For each page, test that all interactive elements are reachable via Tab key:
Permission review
The documentation includes network, browsing, or remote request actions.
$PW -s=a11y run-code 'async (page) => { await page.addScriptTag({ url: "https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js" }); return await page.evaluate(async () => { const r = await axe.run(); return { violations: r.violatEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 9 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Degree of freedom: MIXED — Phases 0–1, 5 [HIGH freedom]; Phases 2–3
playwright/axe/Tab sequences [LOW freedom — run exactly]. Read
protocol-browser-anti-stall before any browser step.
Observe: axe
labelon/signup;<input name="email">has noid/aria-label. Interpret: AT users cannot tell what to type (1.3.1 / 4.1.2). Classify: WCAG fail (not an axe false positive). Severity: P0 — Level A, blocks signup. Finding:/signup| 1.3.1+4.1.2 | P0 | unlabeled email |<label for>.
Glob("**/app/**/page.{tsx,jsx,ts,js}")
Glob("**/pages/**/*.{tsx,jsx,ts,js}")
Glob("**/src/routes/**/*.{tsx,jsx,svelte,vue}")
Grep(pattern: "NEXT_PUBLIC_APP_URL|NEXT_PUBLIC_BASE_URL|VITE_APP_URL|APP_URL|localhost", glob: ".env*")
If no production URL, default to http://localhost:3000 (or detected dev server port).
Grep(pattern: "tailwindcss|@chakra-ui|@mui|@radix-ui|shadcn|bootstrap|bulma", glob: "package.json")
Grep(pattern: "axe-core|@axe-core|eslint-plugin-jsx-a11y|pa11y|lighthouse|@testing-library", glob: "package.json")
Grep(pattern: "aria-|role=|tabIndex|sr-only|visually-hidden", glob: "*.{tsx,jsx,vue,svelte}", output_mode: "count")
Record: APP_URL, PAGES, CSS_FRAMEWORK, COMPONENT_LIB, EXISTING_A11Y_TOOLS, ARIA_USAGE_COUNT.
firecrawl:firecrawl_search
{
"query": "WCAG 2.2 success criteria checklist web accessibility 2026",
"limit": 5
}
firecrawl:firecrawl_search
{
"query": "axe-core automated accessibility testing best practices common false positives",
"limit": 5
}
firecrawl:firecrawl_search
{
"query": "<CSS_FRAMEWORK> <COMPONENT_LIB> accessibility best practices common issues",
"limit": 5
}
Scrape 2–3 authoritative results:
firecrawl:firecrawl_scrape
{
"url": "<BEST_RESULT_URL>",
"formats": ["markdown"],
"onlyMainContent": true
}
context7:resolve-library-id
{
"libraryName": "<COMPONENT_LIB e.g. radix-ui or chakra-ui>"
}
context7:query-docs
{
"libraryId": "<RESOLVED_ID>",
"query": "accessibility ARIA keyboard navigation focus management"
}
For each page discovered in Phase 0a:
PW="npx --yes @playwright/cli@latest"
$PW -s=a11y open --headed "<APP_URL><ROUTE>" # first page; use `goto` for subsequent ones
Apply the protocol-browser-anti-stall protocol: wait 2s, snapshot, verify page loaded.
Use run-code to inject axe-core from CDN and run a full scan:
$PW -s=a11y run-code 'async (page) => { await page.addScriptTag({ url: "https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js" }); return await page.evaluate(async () => { const r = await axe.run(); return { violations: r.violations.map(v => ({ id: v.id, impact: v.impact, description: v.description, help: v.help, helpUrl: v.helpUrl, nodes: v.nodes.length, targets: v.nodes.slice(0, 3).map(n => n.target[0]) })), passes: r.passes.length, incomplete: r.incomplete.length, inapplicable: r.inapplicable.length }; }); }'
For each violation returned, record:
color-contrast, image-alt, label)Heading Hierarchy (WCAG 1.3.1):
$PW -s=a11y eval '() => Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6")).map(h => ({ tag: h.tagName, text: h.textContent.trim().substring(0, 50) }))'
Verify: only one h1, headings don't skip levels (h1 -> h3 without h2).
Image Alt Text (WCAG 1.1.1):
$PW -s=a11y eval '() => Array.from(document.querySelectorAll("img")).map(img => ({ src: img.src.split("/").pop(), alt: img.alt, hasAlt: img.hasAttribute("alt"), decorative: img.getAttribute("role") === "presentation" || img.alt === "" }))'
Verify: all informative images have descriptive alt text, decorative images have alt="" or role="presentation".
Link Text (WCAG 2.4.4):
$PW -s=a11y eval '() => Array.from(document.querySelectorAll("a")).filter(a => ["click here", "here", "read more", "learn more", "", "link"].includes((a.textContent || "").trim().toLowerCase())).map(a => ({ href: a.href, text: (a.textContent || "").trim(), ariaLabel: a.getAttribute("aria-label") }))'
Flag generic link text ("click here", "read more", empty links without aria-label).
Form Labels (WCAG 1.3.1, 4.1.2):
$PW -s=a11y eval '() => Array.from(document.querySelectorAll("input,select,textarea")).map(el => ({ type: el.type, name: el.name, id: el.id, hasLabel: !!el.id && !!document.querySelector(`label[for="${el.id}"]`), ariaLabel: el.getAttribute("aria-label"), ariaLabelledBy: el.getAttribute("aria-labelledby"), placeholder: el.placeholder }))'
Verify: every form control has an associated <label>, aria-label, or aria-labelledby.
Language Attribute (WCAG 3.1.1):
$PW -s=a11y eval '() => ({ htmlLang: document.documentElement.lang, htmlDir: document.documentElement.dir })'
Verify: <html> has a valid lang attribute.
For each page, capture a screenshot as visual evidence:
$PW -s=a11y screenshot --filename ".playwright-mcp/a11y-<route>.png"
For each page, test that all interactive elements are reachable via Tab key:
PW="npx --yes @playwright/cli@latest"
$PW -s=a11y open --headed "<APP_URL><ROUTE>" # first page; use `goto` for subsequent ones
Press Tab repeatedly and snapshot after each press to track focus movement:
$PW -s=a11y press Tab
$PW -s=a11y snapshot
After each Tab, check:
Repeat Tab at least 10-15 times per page to cover the main interactive elements.
For each interactive component (dropdowns, modals, tabs, accordions):
Enter/Space activation (WCAG 2.1.1):
Escape to close (WCAG 2.1.1 for modals/dialogs):
$PW -s=a11y press Escape
Verify: modals/dropdowns close, focus returns to the trigger element.
Arrow key navigation (for tab panels, menus, listboxes):
$PW -s=a11y press ArrowDown
Test that focus is managed correctly after dynamic updates:
sentry:search_issues
{
"organizationSlug": "<ORG_SLUG>",
"projectSlug": "<PROJECT_SLUG>",
"query": "is:unresolved aria OR screenreader OR voiceover OR talkback OR nvda OR jaws OR focus OR tabindex OR keyboard"
}
sentry:search_issues
{
"organizationSlug": "<ORG_SLUG>",
"projectSlug": "<PROJECT_SLUG>",
"query": "is:unresolved keydown OR keyup OR keypress OR focusin OR focusout"
}
If errors cluster on AT-favored browsers:
sentry:get_issue_tag_values
{
"organizationSlug": "<ORG_SLUG>",
"issueId": "<ISSUE_ID>",
"tagKey": "browser"
}
Grep(pattern: "onClick(?!.*role=)(?!.*tabIndex)", glob: "*.{tsx,jsx}")
Flag div or span elements with click handlers but no role or tabIndex — these are invisible to keyboard and screen reader users.
SemanticSearch(query: "Where are custom interactive components defined (dropdowns, modals, tabs, accordions) that don't use a component library?", target_directories: [])
Check each custom interactive for:
role="dialog", role="tabpanel", etc.)onKeyDown)useRef + .focus())aria-expanded, aria-selected, aria-hidden)Grep(pattern: "text-gray-[345]|text-slate-[345]|text-zinc-[345]|text-neutral-[345]|color:\\s*#[789abc]", glob: "*.{tsx,jsx,css,scss}")
Light gray text on white background is the most common contrast failure. Flag files using light gray text colors for manual contrast checking.
═══════════════════════════════════════════════════════
WCAG 2.2 ACCESSIBILITY AUDIT REPORT
Project: <PROJECT_NAME>
Date: <DATE>
Standard: WCAG 2.2 Level AA
Pages Tested: <COUNT>
═══════════════════════════════════════════════════════
## 1. EXECUTIVE SUMMARY
| Metric | Result |
|---------------------------|-----------------|
| Pages tested | X |
| Total violations | N |
| Critical violations | N |
| Serious violations | N |
| axe-core rules passed | N |
| Keyboard navigable pages | X/Y |
| Overall compliance | X% (estimated) |
## 2. AXE-CORE SCAN RESULTS (per page)
| Page | Critical | Serious | Moderate | Minor | Pass | Score |
|------|----------|---------|----------|-------|------|-------|
| / | ... | ... | ... | ... | ... | A-F |
| /about | ... | ... | ... | ... | ... | A-F |
## 3. VIOLATIONS BY WCAG CRITERION
| WCAG SC | Name | Level | Pages Affected | Elements | Impact | Status |
|---------|------|-------|----------------|----------|--------|--------|
| 1.1.1 | Non-text Content | A | ... | ... | ... | PASS/FAIL |
| 1.3.1 | Info and Relationships | A | ... | ... | ... | PASS/FAIL |
| 1.4.3 | Contrast (Minimum) | AA | ... | ... | ... | PASS/FAIL |
| 2.1.1 | Keyboard | A | ... | ... | ... | PASS/FAIL |
| 2.4.3 | Focus Order | A | ... | ... | ... | PASS/FAIL |
| 2.4.4 | Link Purpose | A | ... | ... | ... | PASS/FAIL |
| 2.4.7 | Focus Visible | AA | ... | ... | ... | PASS/FAIL |
| 3.1.1 | Language of Page | A | ... | ... | ... | PASS/FAIL |
| 4.1.2 | Name, Role, Value | A | ... | ... | ... | PASS/FAIL |
## 4. KEYBOARD NAVIGATION
| Page | Tab Order Logical | Focus Visible | No Focus Trap | Skip Link | Score |
|------|-------------------|---------------|---------------|-----------|-------|
| / | ... | ... | ... | ... | A-F |
## 5. INTERACTIVE COMPONENTS
| Component | Location | ARIA Roles | Keyboard Operable | Focus Managed | Score |
|-----------|----------|------------|-------------------|---------------|-------|
| Modal | ... | ... | ... | ... | A-F |
| Dropdown | ... | ... | ... | ... | A-F |
## 6. SENTRY ASSISTIVE TECHNOLOGY ERRORS
| Issue | Error Type | Events | Browser/AT | Fix Needed |
|-------|------------|--------|------------|------------|
| ... | ... | ... | ... | ... |
## 7. STATIC CODE FINDINGS
| Pattern | Files | Count | Risk |
|---------|-------|-------|------|
| onClick without role/tabIndex | ... | N | High |
| Custom interactive without ARIA | ... | N | High |
| Low-contrast text classes | ... | N | Medium |
## 8. CRITICAL FINDINGS (Action Required)
P0 — Level A violations (legal risk):
1. [finding with WCAG SC, element, page, evidence]
P1 — Level AA violations (compliance gap):
1. [finding with WCAG SC, element, page, evidence]
P2 — Best practice improvements:
1. [finding with rationale]
## 9. RECOMMENDATIONS
| # | WCAG SC | Current | Recommended | Effort | Files to Change |
|---|---------|---------|-------------|--------|-----------------|
| 1 | ... | ... | ... | S/M/L | ... |
═══════════════════════════════════════════════════════
Frequently asked questions
Degree of freedom: MIXED — Phases 0–1, 5 [HIGH freedom]; Phases 2–3 playwright/axe/Tab sequences [LOW freedom — run exactly]. Read protocol-browser-anti-stall before any browser step.
The source record exposes this install command: npx skills add https://github.com/kensaurus/cursor-kenji --skill "skills/audit-accessibility". Inspect the command and pinned source before running it.
Static rules flagged network in the source; the page lists the matching lines and excerpts.
Alternatives
mgiovani/cc-arsenal
Multi-agent review team: architecture, security, performance, testing, style, docs/UX, plus an adversary that cross-examines the other 6, for security-sensitive, architectural, or large PRs (15+ files) where a single-agent pass risks missing cross-cutting issues. Use for auth/payments/PII changes, schema/pattern changes, compliance sign-off, or when asked to 'get the review team on this' / 'multi-agent review' / 'thorough review before merge'. For a standard PR or a quick pre-merge check, use /r
th3vib3coder/vibe-science
Scientific research engine for hypothesis testing, literature gap analysis, experimental validation, and data-driven discovery. Enforces adversarial review (Reviewer 2), 32 quality gates, tree search over hypotheses, confounder harness for quantitative claims, and serendipity detection. TRIGGER when: user asks to analyze scientific data, test hypotheses, validate findings, search for research gaps, design experiments, or investigate results. DO NOT TRIGGER when: pure code review, documentation w
upex-galaxy/agentic-qa-boilerplate
Analyze, prioritize, and document test cases in TMS (Jira/Xray), or repair an existing Story-ATS-ATP-ATR-TC cascade through a sealed explicit mode. Use for Test/ATP/ATR artifacts, ROI and automation verdicts, maintaining traceability, fix-traceability, or broken TMS links. The repair-traceability mode audits, plans, waits for explicit approval, applies, and verifies without launching the general documentation workflow. Do NOT use for writing test code (test-automation) or running suites (regress
ArabelaTso/Skills-4-SE
Generates clear and structured pull request descriptions from code changes. Use when Claude needs to: (1) Create PR descriptions from git diffs or code changes, (2) Summarize what changed and why, (3) Document breaking changes with migration guides, (4) Add technical details and design decisions, (5) Provide testing instructions, (6) Enhance descriptions with security, performance, and architecture notes, (7) Document dependency changes. Takes code changes as input, outputs comprehensive PR desc