Help us improve
Share bugs, ideas, or general feedback.
From UX — the design intelligence plugin for Claude Code
Runs a structured WCAG 2.1 AA accessibility audit on a URL, file, or screenshot. Checks contrast, keyboard navigation, focus, ARIA, screen reader support, and motion preferences, then produces a grouped findings report with severity, evidence, and fixes.
npx claudepluginhub laith0003/ux-skill --plugin uxHow this command is triggered — by the user, by Claude, or both
Slash command
/ux:ux-a11yThis command is limited to the following tools:
The summary Claude sees in its command listing — used to decide when to auto-load this command
# /ux-a11y You are running the `/ux-a11y` command from the `ux` plugin. The job is a structured WCAG 2.1 AA audit of a surface, plus the common-courtesy checks that pass automated tools but still hurt real users. ## When to use Triggers: "accessibility check", "WCAG audit", "is this accessible", "a11y review", "screen reader test", "keyboard nav check", "color contrast", "ARIA review", "make this accessible". Use whenever a surface is touched by users beyond the team, especially before shipping any production change. Also use proactively after a redesign — accessibility regressions are ...
/run-auditRuns WCAG 2.1 accessibility audit (AA default) on a URL or component via axe-core and manual checks for keyboard nav, ARIA, contrast, forms, media. Outputs prioritized report with remediation.
/accessibility-auditAudits UI code for WCAG 2.1/2.2 compliance at A/AA/AAA level, identifies issues across perceivable/operable/etc. criteria, and provides remediation. Accepts file/component path or prompts for target.
/a11y-auditAudits accessibility for live URLs, UI components, or screenshots against WCAG A/AA/AAA standards, generating a markdown report with tiered issues and fixes.
/accessibility-auditRuns a full WCAG AA accessibility audit on an HTML file — checks contrast ratios, keyboard navigation, semantic HTML, ARIA usage, touch targets, and label associations.
/auditRuns a full web accessibility audit using the web-accessibility-wizard agent with all specialists, producing a prioritized report.
Share bugs, ideas, or general feedback.
You are running the /ux-a11y command from the ux plugin. The job is a structured WCAG 2.1 AA audit of a surface, plus the common-courtesy checks that pass automated tools but still hurt real users.
Triggers: "accessibility check", "WCAG audit", "is this accessible", "a11y review", "screen reader test", "keyboard nav check", "color contrast", "ARIA review", "make this accessible".
Use whenever a surface is touched by users beyond the team, especially before shipping any production change. Also use proactively after a redesign — accessibility regressions are the most common silent failure.
One of: a URL, an absolute file path (Blade view, JSX, HTML), a screenshot, a code snippet. If a live URL is available, prefer it — automated tools and keyboard testing only work on live surfaces. If only static markup is available, the audit will be partial and you must flag what could not be verified.
Surface the mode at the top of the report so the user knows what could not be verified.
For each item, check the relevant WCAG success criterion (cite the SC number) and decide pass / fail / partial / not-verifiable.
outline: none without a replacement.<button>, <nav>, <main>, <header>, not all <div>).<label> (explicit for or wrapping).alt="", meaningful images have descriptive alt.aria-label / aria-labelledby on every unlabeled control.aria-live regions for async updates.aria-expanded not updated, aria-hidden on focusable elements).prefers-reduced-motion media query respected.aria-live or aria-describedby).<ul> / <ol> / <li>.<table> with <th> and proper scope.<button>, links are <a> with href.<img> has an alt attribute (even if empty).alt="".For each failing or partial item, assign severity:
─── a11y audit ───
Surface: <URL / path / description>
Mode: <live URL | code | screenshot>
Verified: <list of points fully verified>
Partial: <list of points partially verified — explain why>
Not verified: <list of points that could not be verified in this mode>
Severity counts: Critical <n> | High <n> | Medium <n> | Cosmetic <n>
─── findings ───
WCAG <SC number> — <SC name>
[<severity>] <finding title>
Evidence: <what you saw — include selector or line number if code>
Fix: <what to do — be specific>
WCAG ...
...
─── beyond WCAG ───
<common-courtesy findings that pass spec but still hurt users — e.g., autoplay video, tiny tap targets, time-limited dialogs>
Write .ux/last-a11y.json:
{
"command": "ux-a11y",
"timestamp": "<ISO8601>",
"surface": "<URL / path / description>",
"mode": "live | code | screenshot",
"verified_points": ["<list of point names>"],
"partial_points": ["<list>"],
"not_verified": ["<list>"],
"findings": [
{
"wcag_sc": "1.4.3",
"sc_name": "Contrast (Minimum)",
"severity": "Critical | High | Medium | Cosmetic",
"title": "<short>",
"evidence": "<what you saw>",
"fix": "<what to do>",
"category": "code | copy | motion | other"
}
],
"beyond_wcag": [
{ "title": "<short>", "evidence": "<>", "fix": "<>" }
],
"severity_counts": { "critical": <n>, "high": <n>, "medium": <n>, "cosmetic": <n> }
}
The category field tells /ux-fix which sub-agent to dispatch.
If the user passed --fix, after writing the report:
--include-cosmetic):
category: code → dispatch frontend-engineer.category: copy → dispatch copy-writer.category: motion → dispatch motion-engineer.The audit report and (if --fix) the fix-loop results.
.ux/last-a11y.json — keys: command, timestamp, surface, mode, verified_points, partial_points, not_verified, findings (array of {wcag_sc, sc_name, severity, title, evidence, fix, category}), beyond_wcag (array), severity_counts.─── next ───
Recommended: /ux-a11y --fix (apply the fixes)
Other moves: /ux-copy (form-error wording deep-dive)
/ux-motion (reduced-motion compliance deep-dive)
/ux-audit (broader structural review)
/ux-next (let me decide)
If not_verified is non-empty and a live URL is feasible, recommend re-running on the live URL before fixing — partial audits produce regressions.
not_verified and explain.beyond_wcag section — passing WCAG is the floor, not the ceiling.not_verified items. Verify first, then fix.aria-label on a <button> that already has visible text. The text IS the label. Adding ARIA duplicates it.prefers-reduced-motion because the animation "looks fine." That is the wrong test — the test is whether motion-sensitive users can use the surface.| Error condition | Recovery |
|---|---|
| Surface is JS-rendered and the crawler can't see the DOM | Ask the user to paste rendered HTML or send a screenshot of the live DOM |
| Contrast tooling not available | Describe the ratio check verbally (sample foreground + background hex, compute ratio), flag as partial |
| Screenshot-only mode for points requiring live DOM (keyboard, screen reader, dynamic states) | Mark those points not_verified and explain in the report; do not silently pass |
| Color samples ambiguous (gradient, transparency, image background) | Sample worst-case area, note assumption, flag as partial |
--fix requested on items marked not_verified | Refuse; require verification first |
| Live URL unreachable | Fall back to code or screenshot, mark mode change in report |
For path issues: see references/process/discovery-protocol.md for state file location (.ux/ in project root). Report bugs at https://github.com/Laith0003/ux-skill/issues.
Before producing any judgment, the LLM running this command MUST shell to the v2 Python engine to ground its work in deterministic rules. The mechanical pass runs first; the taste pass runs second.
python3 -m engine.cli.main --no-pretty lint <user-supplied-path> --threshold high > /tmp/ux-lint-report.json 2>/dev/null \
|| bash bin/ux-lint.sh <user-supplied-path>
The Python linter reads rules from data/anti-patterns.json (35 regex rules across 8 categories). It returns structured JSON with findings keyed by file:line:column.
cat /tmp/ux-lint-report.json | python3 -c "
import json, sys
r = json.load(sys.stdin)
s = r['summary']
print(f\"scanned: {r['files_scanned']} files, {r['rules_loaded']} rules\")
print(f\" critical: {s.get('critical', 0)}\")
print(f\" high: {s.get('high', 0)}\")
print(f\" medium: {s.get('medium', 0)}\")
print(f\" low: {s.get('low', 0)}\")
print(f\" total: {s.get('total', 0)}\")
for f in r['findings'][:20]:
print(f\" [{f['severity']}] {f['file']}:{f['line']} {f['rule_name']} ({f['rule_id']})\")
"
Filter the linter report for A11y-category findings:
cat /tmp/ux-lint-report.json | python3 -c "
import json, sys
r = json.load(sys.stdin)
a11y = [f for f in r['findings'] if f['category'] == 'A11y']
print(f'A11y findings: {len(a11y)}')
for f in a11y:
print(f\" [{f['severity']}] {f['file']}:{f['line']} — {f['rule_name']}\")
print(f\" fix: {f['fix']}\")
"
Pull accessibility guidelines from data/ux-guidelines.json (the 8 entries under category=Accessibility). Those are your reference for what WCAG and ARIA gaps to call out beyond what regex caught.
Take the structured findings from Step 1 and any data the engine returned in Step 3, and use those AS YOUR INPUT to the LLM-side reasoning. Do NOT re-derive what the linter already proved — the regex pass is the truth on those rules. Your job is the taste-level judgment the linter cannot make.
If python3 -m engine.cli.main is not on PATH, fall back to bash bin/ux-lint.sh for the linter pass and v1 prose-only behavior for everything else.