| name | ux-audit |
| description | Heuristic usability and accessibility audit of existing web pages — load a rendered page, evaluate it against Nielsen's 10 usability heuristics + WCAG 2.2 (A/AA) + responsive behavior + the page's own goal, and return prioritized findings with a concrete fix for each. Use whenever the task is to review, audit, critique, or "find usability/UX problems" on one or more web pages, do an accessibility (a11y) check, assess responsiveness, or propose usability fixes — even if the user just says "is this page any good", "what's wrong with this UI", "make this more usable", or hands you a URL/screenshot. This is a review/audit method, not a design-decision debate (that's design-council) and not functional testing (that's webapp-testing). Read by the ux-auditor agent, which renders each page with a browser driver and fans out one auditor per page. |
| license | MIT |
UX Audit
Audit an existing, rendered page against a fixed rubric and return findings the caller
can act on. The discipline that makes this useful: audit what's actually on screen, cite
the heuristic or success criterion, and propose a concrete fix. A finding with no
location and no fix is noise; a "problem" you assumed without observing the page is worse.
This is distinct from neighbours:
- design-council deliberates one high-stakes design decision (preserves dissent).
Use it for "should we restructure this flow?", not "audit these 12 pages."
- webapp-testing checks that features work. UX audit checks whether they're usable
and accessible even when they work.
Inputs you need
- The page(s): a URL, a local file, or a running dev server. For a series of pages, the
ux-auditor agent fans out — one auditor per page (see that agent).
- The page's goal — what is the user there to do? You can't judge usability without it.
If not given, infer it from the page and state your assumption in the finding set.
- Viewports to check (at least one mobile ~375px and one desktop ~1280px). Responsive
problems only show up if you actually resize.
Render the page first — don't audit blind
Audit the rendered DOM and the visual result, not the raw source. Use whatever browser
driver is installed (prefer agent-browser, fall back to playwright-cli; the CLI binary
is playwright-cli, not playwright):
if command -v agent-browser >/dev/null 2>&1; then echo agent-browser
elif command -v playwright-cli >/dev/null 2>&1; then echo playwright-cli
else echo NONE; fi
For each page capture: a screenshot at each viewport, the rendered HTML/DOM, and
(where the driver allows) computed styles for contrast and a tab-order walk for keyboard
checks. The screenshot is your evidence; reference it in findings.
Isolate the session — the #1 cause of bad audits under fan-out. Both drivers default
to a single shared browser session via a background daemon. When several audits run at once
(the fan-out pattern), they collide on that shared session and you get cross-contamination
— evals returning another page's DOM. Always give each audit its own named session and
close it when done. This, not the tool, is what broke an early run of this skill.
Time-box the driver. If it still doesn't render cleanly within a few tries, switch
drivers or drop to a static audit and say so — don't sink 30 calls into fighting it.
agent-browser recipe (preferred — handles local files and concurrency cleanly)
S="uxaudit-$$"
agent-browser --session "$S" --allow-file-access open "file:///abs/path/page.html"
agent-browser --session "$S" --allow-file-access open "https://example.com"
- Local files load directly with
--allow-file-access — no HTTP server needed.
eval via --stdin avoids all shell-quoting corruption (the trap that wastes calls);
return a string with JSON.stringify(...):
agent-browser --session "$S" eval --stdin <<'EOF'
JSON.stringify((() => {
const noAlt = Array.from(document.images).filter(i => !i.alt).length;
const p = document.querySelector("p"); const s = getComputedStyle(p);
const b = document.querySelector("button"); const r = b.getBoundingClientRect();
return { noAlt, color: s.color, bg: s.backgroundColor, target: r.width + "x" + r.height };
})())
EOF
- Evidence & viewports:
snapshot -i (a11y refs/roles), screenshot --annotate
(numbered labels — ideal for icon-only buttons), screenshot --full. For mobile, set
viewport via config/flags or audit at the iOS profile (-p ios).
- Always
agent-browser --session "$S" close when done.
playwright-cli recipe (fallback — binary is playwright-cli, not playwright)
- Serve local files over HTTP —
file:// URLs often don't load. Start a throwaway
server and target http://localhost:
( cd "$DIR" && python3 -m http.server 8731 & )
playwright-cli open
playwright-cli goto "http://localhost:8731/page.html"
open <url> may stay on about:blank; always goto explicitly after open.
eval takes a function and the value prints under ### Result. Use () => ...
form; a bare expression containing => is misparsed as a function-to-apply. Return a
string (JSON.stringify(...)) so the result is unambiguous:
playwright-cli eval "() => { const b=document.querySelector('button'); const r=b.getBoundingClientRect(); return JSON.stringify({w:r.width,h:r.height}); }"
- High-yield evals: images missing
alt, inputs whose .labels is empty, computed
color/backgroundColor for contrast, getBoundingClientRect() for target size.
- Viewports & evidence:
playwright-cli resize 375 812 then screenshot, repeat at
1280 900. playwright-cli snapshot dumps the accessibility tree (names/roles).
- Always
playwright-cli close when done.
If no driver is installed (or you abandoned a flaky one), set "driver": "static-only",
audit the static HTML + any screenshot the user provides, and flag in notes that
dynamic/contrast/keyboard/viewport checks were not performed. An honest partial audit beats
a guess.
The rubric
Walk all three lenses. Don't stop at accessibility — usability and responsiveness catch
different failures.
1. Nielsen's 10 usability heuristics
- Visibility of system status — does the UI show what's happening (loading, saved,
selected, where-am-I)?
- Match with the real world — language/concepts the user knows, not system jargon.
- User control & freedom — undo, cancel, back, clear exits from unwanted states.
- Consistency & standards — same thing looks/acts the same; platform conventions.
- Error prevention — constraints, confirmations, sensible defaults that stop mistakes.
- Recognition over recall — options visible; don't make users remember across steps.
- Flexibility & efficiency — accelerators for experts without blocking novices.
- Aesthetic & minimalist design — no competing clutter; signal over noise.
- Help users recover from errors — plain-language errors that say what and how to fix.
- Help & documentation — findable, task-oriented help where needed.
2. WCAG 2.2 — target Level A & AA
Check the high-yield criteria first:
- Text alternatives — meaningful
alt on informative images; empty alt="" on
decorative ones.
- Contrast (1.4.3 / 1.4.11) — text ≥ 4.5:1 (≥ 3:1 for large text ≥ 18.66px bold /
24px); UI components & graphics ≥ 3:1. Measure from computed colours, don't eyeball.
- Keyboard (2.1.1) & focus visible (2.4.7) — everything operable by keyboard; a
visible focus indicator; logical tab order; no keyboard traps.
- Labels & names (1.3.1 / 4.1.2) — form controls have associated
<label>s; controls
expose an accessible name; ARIA used correctly (or not at all).
- Headings & structure (1.3.1 / 2.4.6) — one logical heading outline; landmarks
(
main, nav, header); descriptive page <title>.
- Target size (2.5.8, new in 2.2) — interactive targets ≥ 24×24 CSS px (or spaced).
- Resize/reflow (1.4.10) — usable at 320px width / 400% zoom without horizontal scroll.
- Don't rely on colour alone (1.4.1) — state shown by more than hue.
3. Responsive & device behaviour
- Layout holds at mobile (~375px) and desktop (~1280px); no overflow, overlap, or cut-off
controls. Tap targets aren't cramped. Content reflows rather than requiring zoom/scroll.
Severity — so the caller can triage
Match the fleet's review scale (same as code-reviewer):
- blocking — users can't complete the page's goal, or a WCAG Level A failure shuts
out a group (e.g. unlabeled critical control, keyboard trap, no alt on the only CTA icon).
- major — serious friction or a WCAG AA failure (contrast below 4.5:1, focus not
visible, confusing error with no recovery).
- minor — polish: inconsistency, clutter, small-but-real friction.
Reserve blocking for "this stops someone," and back every finding with what you observed.
Return format (final message) — JSON only
One object per audited page. (When fanned out, the agent merges these.)
{
"page": "url-or-path",
"goal": "what the user is here to do (stated or inferred)",
"viewports_checked": ["375x812", "1280x900"],
"driver": "agent-browser | playwright-cli | static-only",
"summary": { "blocking": 0, "major": 0, "minor": 0 },
"findings": [
{
"severity": "blocking | major | minor",
"lens": "usability | accessibility | responsive",
"ref": "Nielsen #5 | WCAG 1.4.3 | responsive@375px",
"location": "selector or region, e.g. 'form#signup > button.submit'",
"evidence": "what you observed (+ screenshot ref / measured value)",
"suggested_fix": "concrete change to make",
"confidence": "high | medium | low"
}
],
"notes": "assumptions, what couldn't be checked, empty if none"
}
Be precise over exhaustive. Ten located, fixable findings beat forty vague ones. If you
couldn't render the page, say what you could and couldn't assess rather than inventing
problems.
Privacy
The audited pages and any findings about a specific product are often the client's
property — keep the page content and results out of any shared/committed location. This
skill and the ux-auditor agent are generic and publishable; the data they process is not.