
Ux Audit
Run mandatory axe-core accessibility scans on every audited page during polish so structural a11y bugs are caught in seconds, not only in manual keyboard walks.
Overview
ux-audit is an agent skill most often used in Ship (also Build frontend polish) that runs axe-core automated accessibility checks on each audited page and maps violations into a structured audit severity log.
Install
npx skills add https://github.com/jezweb/claude-skills --skill ux-auditWhat is this skill?
- Injects axe-core 4.10.0 via CDN and runs axe.run() in browser context
- Mandatory in Phase 4 (Polish) for every page in the audit scope
- Maps axe impact levels to audit severity buckets for consistent logging
- Captures violations, incomplete rules, and pass counts per page in under one second
- Complements manual keyboard-only walks that miss roughly 80% of structural a11y issues
- Manual keyboard-only walks miss roughly 80% of structural accessibility bugs
- axe-core coverage runs in under 1 second per page
- Uses axe-core 4.10.0 from CDN injection pattern
Adoption & trust: 1.6k installs on skills.sh; 841 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
Manual UX walks catch interaction bugs but leave most structural accessibility failures undetected until users or lawsuits surface them.
Who is it for?
Indie builders finishing UI polish who need WCAG-oriented automation on every page without a dedicated a11y team.
Skip if: Teams treating axe alone as full compliance sign-off without keyboard, screen-reader, or content review passes.
When should I use this skill?
User is in Phase 4 (Polish) or requests automated accessibility testing with axe-core on audited pages.
What do I get? / Deliverables
Each audited page gets a fast axe-core violation report with mapped severities, ready to fold into Phase 4 polish fixes alongside manual findings.
- Per-page axe violations array with impact, help URL, and sample node HTML
- Counts of passes, incomplete rules, and logged severity-mapped findings
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Ship/testing is canonical because axe automation is mandated in Phase 4 (Polish) as part of pre-release quality gates. Testing subphase covers automated regression-style checks that complement manual scenario walks for accessibility compliance.
Where it fits
Scan all marketing and app routes with axe.run() the night before a v1 launch.
Attach mapped axe severities to a polish checklist shared with the agent for fix prioritization.
Re-run the injection script after refactoring a modal to confirm aria and focus fixes.
Audit newly published help-center pages for heading order and label gaps.
How it compares
Use alongside manual keyboard scenarios—axe covers bulk structural rules, not the full interactive audit by itself.
Common Questions / FAQ
Who is ux-audit for?
Solo developers and small teams shipping web UIs who want a scripted axe-core pass integrated into a formal UX audit during polish.
When should I use ux-audit?
During Ship testing on every page in scope for Phase 4 Polish, and during Build frontend iterations when you want quick regression scans after UI changes.
Is ux-audit safe to install?
The workflow loads axe-core from a CDN and runs scripts in the browser context; review the Security Audits panel on this Prism page and your CSP policies before production use.
SKILL.md
READMESKILL.md - Ux Audit
# Automated Accessibility (axe-core) Manual keyboard-only walks (Scenario 5) catch focus traps and tab-order problems. They miss roughly 80% of structural a11y bugs — heading skips, contrast at hover/focus state, missing aria-labels, dynamic-content announcements, role mismatches. axe-core covers that 80% in <1 second per page. This is **mandatory in Phase 4 (Polish)** for every audited page. ## The protocol For every page audited: 1. Navigate to the page. 2. Inject axe-core (single CDN script tag). 3. Run `axe.run()` in the browser context. 4. Capture violations array. 5. Map severity → audit severity (table below) and log findings. ```js // In the browser eval / playwright-cli / chrome MCP await page.evaluate(async () => { // Inject axe-core if not present if (!window.axe) { await new Promise((resolve, reject) => { const s = document.createElement('script') s.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.0/axe.min.js' s.onload = resolve s.onerror = reject document.head.appendChild(s) }) } const results = await window.axe.run() return { violations: results.violations.map(v => ({ id: v.id, impact: v.impact, description: v.description, help: v.help, helpUrl: v.helpUrl, nodes: v.nodes.length, sample: v.nodes[0]?.html?.substring(0, 200), })), incomplete: results.incomplete.length, passes: results.passes.length, } }) ``` ## Severity mapping axe reports `impact` per violation. Map to audit severity: | axe `impact` | Audit severity | Hard-gate? | |---|---|---| | `critical` | Critical | YES — audit auto-fails | | `serious` | High | YES — audit auto-fails | | `moderate` | Medium | No — finding, not gate | | `minor` | Low | No — finding, not gate | ## Hard-gate threshold Add to the SKILL.md hard gates table: | Gate | Threshold | Severity if violated | |---|---|---| | axe-core Critical violations | > 0 | Critical | | axe-core Serious violations | > 0 | High | A page with > 0 axe Critical or > 0 axe Serious cannot pass the audit. axe Moderate / Minor accumulate as Medium / Low findings respectively. ## Allowlist for known-noise Some axe rules fire on intentional design choices that aren't real bugs (e.g. `landmark-one-main` on a chrome-less embed page, `region` on a wrapper that intentionally has no landmark). Allowlist via `.jez/audit-config.yml`: ```yaml axe: ignore_rules: - landmark-one-main # iframe-style embed pages - region # legacy wrapper, refactor scheduled 2026-Q3 ignore_pages: - /dashboard/components # builder-mode, intentional - /dashboard/style-guide # builder-mode, intentional ``` Default with no config: every axe Critical / Serious is a finding. Allowlist is opt-in. ## Findings format Each axe violation becomes a finding in the report: ``` ID: H-N Layer: Visual / Interaction (a11y) Severity: High (axe Serious) Surface: /dashboard/inbox axe rule: color-contrast Description: Elements must have sufficient color contrast (WCAG AA: 4.5:1 normal, 3:1 large) Help: https://dequeuniversity.com/rules/axe/4.10/color-contrast Nodes affected: 3 Sample: <button class="btn-secondary">Mark all read</button> Computed contrast: 3.2:1 (text-muted-foreground on bg-muted) Required: 4.5:1 Reproduce: 1. Navigate to /dashboard/inbox 2. Inspect "Mark all read" button (or any .btn-secondary) 3. Read computed colour ratios in DevTools Suspected location: src/components/ui/button.tsx — secondary variant; or the --muted-foreground token in src/styles/globals.css Suggested fix: bump --muted-foreground from oklch(0.55 0 0) to oklch(0.45 0 0) or change secondary variant to use --foreground. ``` ## What axe doesn't catch axe is structural. It misses: - Whether the screen-reader announcement actually conveys meaning - Whether dynamic content (toasts, live updates) announces to AT - Whether keyboard tab-order makes sense (axe checks focusability, not