agentsclimarketplace

Audit accessibility

Skill kensaurus/cursor-kenji/skills/audit-accessibility

πŸ¦–Curated Cursor AI agent skills, slash commands, MCP configs, subagents & rules for full-stack dev β€” React 19, Next.js 15, Supabase, Tailwind v4, TypeScript

Install
npx -y skills add kensaurus/cursor-kenji --skill audit-accessibility

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Automated WCAG 2.2 accessibility audit using Playwright browser MCP to crawl every page, inject axe-core via browser_evaluate, test keyboard navigation, check color contrast, ARIA labels, heading hierarchy, form labels, and focus management. Cross-references Sentry for assistive-technology-related errors, researches current WCAG guidelines via Firecrawl, and produces a structured PASS/FAIL compliance report per criterion. Works with any project β€” auto-detects pages from the route manifest. Use when asked to: "audit accessibility", "check a11y", "WCAG audit", "check keyboard nav", "test screen reader", "accessibility compliance", "ADA compliance check", "run a11y audit", "check color contrast", or "audit for disabilities".

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

14.8 KB, as published. Nobody here has run it

Accessibility Audit

Automated WCAG 2.2 accessibility audit: axe-core scanning, keyboard navigation testing, color contrast verification, ARIA compliance, and assistive technology error analysis. Works with any project β€” auto-detects pages from the codebase route structure.

Critical Rules

Always use the browser-anti-stall protocol when using Playwright browser MCP tools.

Test every publicly reachable page. Accessibility bugs on obscure pages still affect real users.

Keyboard-only testing is non-negotiable. If a feature cannot be operated without a mouse, it fails WCAG 2.1.1 regardless of what axe-core says.

Automated tools catch ~30-40% of a11y issues. Always supplement axe-core with manual checks (heading order, focus management, meaningful link text, reading order).

Use concrete WCAG success criteria IDs. "Color contrast is bad" is not actionable. "Fails WCAG 2.2 SC 1.4.3 (Contrast Minimum) β€” foreground #777 on background #fff = 4.48:1, needs 4.5:1" is.


Phase 0: Auto-Detect Pages and Components

0a. Discover Route Structure

Glob("**/app/**/page.{tsx,jsx,ts,js}")
Glob("**/pages/**/*.{tsx,jsx,ts,js}")
Glob("**/src/routes/**/*.{tsx,jsx,svelte,vue}")

0b. Find the App URL

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).

0c. Detect CSS Framework and Component Library

Grep(pattern: "tailwindcss|@chakra-ui|@mui|@radix-ui|shadcn|bootstrap|bulma", glob: "package.json")

This matters because each framework has different a11y defaults (e.g., Radix primitives are accessible by default, custom components may not be).

0d. Detect Existing a11y Tooling

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 β€” base URL for testing
  • PAGES β€” list of routes to audit
  • CSS_FRAMEWORK β€” Tailwind, MUI, Chakra, etc.
  • COMPONENT_LIB β€” Radix, shadcn, MUI, Headless UI, etc.
  • EXISTING_A11Y_TOOLS β€” what's already in place
  • ARIA_USAGE_COUNT β€” rough indicator of a11y awareness in the codebase

Phase 1: Research Current WCAG Guidelines

1a. Firecrawl Research

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 the 2-3 most authoritative results:

firecrawl:firecrawl_scrape
{
 "url": "<BEST_RESULT_URL>",
 "formats": ["markdown"],
 "onlyMainContent": true
}

1b. Framework-Specific a11y Docs (Context7)

If the project uses a component library, fetch its accessibility documentation:

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"
}

Phase 2: Automated Scanning (axe-core via Playwright)

2a. Navigate to Each Page

For each page discovered in Phase 0a:

playwright:browser_navigate
{
 "url": "<APP_URL><ROUTE>"
}

Apply the browser-anti-stall protocol: wait 2s, snapshot, verify page loaded.

2b. Inject and Run axe-core

Use browser_evaluate to inject axe-core from CDN and run a full scan:

playwright:browser_evaluate
{
 "javascript": "await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js'; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); const results = await axe.run(); return JSON.stringify({ violations: results.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: results.passes.length, incomplete: results.incomplete.length, inapplicable: results.inapplicable.length });"
}

For each violation returned, record:

  • Rule ID (e.g., color-contrast, image-alt, label)
  • Impact (critical, serious, moderate, minor)
  • WCAG criteria mapped from the rule
  • Affected elements (CSS selectors)
  • Count of affected nodes

2c. Check Specific WCAG Categories Manually

axe-core cannot catch everything. For each page, also check:

Heading Hierarchy (WCAG 1.3.1):

playwright:browser_evaluate
{
 "javascript": "return JSON.stringify(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):

playwright:browser_evaluate
{
 "javascript": "return JSON.stringify(Array.from(document.querySelectorAll('img')).map(img => ({ src: img.src.substring(img.src.lastIndexOf('/') + 1), 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):

playwright:browser_evaluate
{
 "javascript": "return JSON.stringify(Array.from(document.querySelectorAll('a')).filter(a => { const text = (a.textContent || '').trim().toLowerCase(); return text === 'click here' || text === 'here' || text === 'read more' || text === 'learn more' || text === '' || text === 'link'; }).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):

playwright:browser_evaluate
{
 "javascript": "return JSON.stringify(Array.from(document.querySelectorAll('input,select,textarea')).map(el => ({ type: el.type, name: el.name, id: el.id, hasLabel: !!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):

playwright:browser_evaluate
{
 "javascript": "return JSON.stringify({ htmlLang: document.documentElement.lang, htmlDir: document.documentElement.dir });"
}

Verify: <html> has a valid lang attribute.

2d. Take Evidence Screenshots

For each page, capture a screenshot as visual evidence:

playwright:browser_take_screenshot
{}

Phase 3: Keyboard Navigation Testing

3a. Tab Order Test

For each page, test that all interactive elements are reachable via Tab key:

playwright:browser_navigate
{
 "url": "<APP_URL><ROUTE>"
}

Press Tab repeatedly and snapshot after each press to track focus movement:

playwright:browser_press_key
{
 "key": "Tab"
}
playwright:browser_snapshot
{}

After each Tab, check:

  • Focus is visible (there is a focus indicator on the active element)
  • Focus order is logical (follows visual reading order)
  • No focus trap (Tab eventually cycles back to the beginning or reaches the end)
  • Skip links exist (if there is a large navigation area)

Repeat Tab at least 10-15 times per page to cover the main interactive elements.

3b. Interactive Element Testing

For each interactive component (dropdowns, modals, tabs, accordions):

Enter/Space activation (WCAG 2.1.1):

  • Tab to the element
  • Press Enter or Space
  • Verify the action fires (snapshot to confirm state change)

Escape to close (WCAG 2.1.1 for modals/dialogs):

playwright:browser_press_key
{
 "key": "Escape"
}

Verify: modals/dropdowns close, focus returns to the trigger element.

Arrow key navigation (for tab panels, menus, listboxes):

playwright:browser_press_key
{
 "key": "ArrowDown"
}

3c. Focus Management After State Changes

Test that focus is managed correctly after dynamic updates:

  • After opening a modal: focus should move into the modal
  • After closing a modal: focus should return to the trigger
  • After deleting an item from a list: focus should move to the next item or a logical location
  • After form submission: focus should move to a success/error message

Phase 4: Cross-Check with Sentry

4a. Search for Assistive Technology Errors

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"
}

4b. Check for JavaScript Errors on Keyboard Interaction

sentry:search_issues
{
 "organizationSlug": "<ORG_SLUG>",
 "projectSlug": "<PROJECT_SLUG>",
 "query": "is:unresolved keydown OR keyup OR keypress OR focusin OR focusout"
}

4c. Browser/Device Breakdown

Check if errors disproportionately affect specific browsers or devices that assistive technology users favor:

sentry:get_issue_tag_values
{
 "organizationSlug": "<ORG_SLUG>",
 "issueId": "<ISSUE_ID>",
 "tagKey": "browser"
}

Phase 5: Static Code Analysis

Supplement runtime testing with static analysis of the source code.

5a. Missing ARIA Attributes

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.

5b. Inaccessible Custom Components

SemanticSearch(query: "Where are custom interactive components defined (dropdowns, modals, tabs, accordions) that don't use a component library?", target_directories: [])

Custom components are the highest risk for a11y issues. Check each for:

  • Proper ARIA roles (role="dialog", role="tabpanel", etc.)
  • Keyboard event handlers (onKeyDown)
  • Focus management (useRef + .focus())
  • ARIA states (aria-expanded, aria-selected, aria-hidden)

5c. Color Contrast in CSS/Tailwind

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.


Phase 6: Report

═══════════════════════════════════════════════════════
 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 | ... |

═══════════════════════════════════════════════════════

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.