# UI Audit Method

> How to audit a UI codebase by measurement instead of impression. Covers the five counting pitfalls that produce wrong numbers (subset filters, regex eating template syntax, scraping legitimate values, missed optional suffixes, summed opacity variants), render-time verification in a real browser, the screenshot trap where OS display scaling manufactures a layout bug that does not exist, and a per-project baseline file that records metrics and verdicts so the same argument is not re-litigated every audit. Use when asked to audit, measure, or review a UI codebase, when a layout looks broken, or when checking whether color/state classes actually apply. Triggers: UI audit, design audit, measure this UI, layout looks off, UI 감사, 디자인 점검, 레이아웃이 깨져 보인다. This skill does NOT decide what good design is — the project baseline does.

- Skill: `rhino-ty/ui-audit-method` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add rhino-ty/ui-audit-method`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rhino-ty/ui-audit-method/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: MIT
- Author: rhino-ty (https://skillmd.com/u/rhino-ty)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/rhino-ty/ui-audit-method

---


# UI Audit Method

This skill does not tell you what good UI looks like. It tells you how to
**measure** it, how to **verify** what you measured, and how to **record**
verdicts so they stick. What counts as a problem is the project's call.

One principle: **never report an impression.** "This looks kind of AI-generated"
is not a finding. `rounded-xl, 25 occurrences` is.

## Two failure modes

Read this before starting. There are only two ways this work goes wrong.

1. **Inventing problems.** Flagging clean metrics and manufacturing work.
   A metric sitting at zero is not a blank to fill — it is **a state to
   preserve**.
2. **Forgetting settled verdicts.** An item closed last time as "checked, not a
   problem" comes back next audit. That is why verdicts go in a file.

## The baseline comes first

This skill is half a tool on its own. Without knowing what is normal for the
project, you produce numbers with nothing to compare them against.

```
<project>/.claude/ui-baseline.md
```

If it does not exist, copy `references/BASELINE-TEMPLATE.md` and fill it in.
Four things live there:

- **What this app is** — web or desktop, fixed or fluid viewport, how many themes
- **What must not be touched** — palette tokens, framework idioms, existing rules
- **Metrics** — what this project decided to count, why each one matters here,
  and the values at audit time versus now. **The "now" column is the
  regression line.**
- **Verdicts** — items closed as "not a problem", the evidence, and the
  condition that would reopen them

Choosing metrics is the project's job. This skill supplies the counting.

## 1. Establish what you are scanning

Decide what the commands read before you run them. UI lives in different
extensions per framework.

```bash
for e in svelte vue jsx tsx astro html css scss; do
  n=$(git ls-files "*.$e" | wc -l); [ "$n" -gt 0 ] && echo "  .$e $n"
done
```

Examples below use `*.svelte`. Adjust `--include` to match.

## 2. Counting — and five pitfalls

Whatever the metric, these traps apply. **Every one produced a wrong number in
real use.**

### Pitfall 1 — counting a subset and believing it is the whole

Filtering by size or shape silently drops other forms of the same problem.

```bash
grep -rn "text-[45]xl" src --include=*.svelte    # large ones only -> 9
# exhaustive scan -> 85. Everything embedded in button labels and status
# badges was missing from the first number.
```

**Count exhaustively first, classify second.** Never let the filter define the
population.

### Pitfall 2 — the regex eats template syntax

```bash
grep -rn "#[0-9a-fA-F]\{3,8\}"    src --include=*.svelte   # 14 (false)
grep -rn "#[0-9a-fA-F]\{3,8\}\b"  src --include=*.svelte   # 2  (real)
```

Without `\b`, Svelte's `{#each}` matches as `#eac`. Vue's `#default` and JSX
`#region` comments leak the same way. **State the boundary.**

### Pitfall 3 — scraping values that are legitimate

```bash
grep -rn "\[[0-9]*px\]"       src --include=*.svelte   # 11 — includes layout sizes
grep -rn "text-\[[0-9]*px\]"  src --include=*.svelte   # 7  — fonts only
```

`w-[260px]` is correct in a fixed-size app. **If you do not know what is normal
you are counting noise, not a metric.** Hence the baseline first.

### Pitfall 4 — missing the bare form

```bash
grep -rho  "rounded-[a-z0-9]*"    src --include=*.svelte | sort -u   # 4 kinds
grep -rhoE "rounded(-[a-z0-9]+)?" src --include=*.svelte | sort -u   # 5 kinds
```

Catching suffix-less forms (`rounded`, `border`, `shadow`) needs `-E` and `?`.

### Pitfall 5 — summing modifier variants

```bash
grep -rhoE "bg-accent/?[0-9]*" src --include=*.svelte | grep -c '^bg-accent$'
```

Counting `bg-accent/80` and friends turns 21 into 39. Hover-only classes are
the same story — folding something invisible at rest into an exposure count
manufactures an overuse finding.

## 3. Verify by rendering (only when layout or color is in question)

If you are only counting class names, skip this. When layout is suspect or you
need to know whether a color actually applies, follow
`references/RENDER-VERIFICATION.md`.

One rule from there matters enough to state up front — **never argue a layout
bug from a screenshot.** On a display scaled above 100%, capturing at the
window's logical size crops the right edge and manufactures an overflow that
is not there. This has produced a false report in practice.

> **When a screenshot and a measurement disagree, believe the measurement.**
> If the same code is fine in a browser but looks broken in the app, suspect
> the observation, not the code.

## 4. Report

Give `file:line` and **the replacement code**. Never write "consider improving".

```
src/components/Card.tsx:63    transition-all
  -> only colors change here. transition-colors
```

Compare against the baseline's "now" column and say whether this is **better or
worse**. A list of absolute numbers gives the reader nothing to judge.

## 5. Fix

**Do not edit without approval.** Wait for an explicit "fix it".

One category per commit. Radius work is radius only; transition work is
transition only. Mixed commits cannot be reverted.

**Always check whether a blanket replacement is safe** — the same class often
appears for different reasons. In one case 5 of 12 `transition-all` hits were
progress bars whose width genuinely animates; a blanket rewrite to
`transition-colors` would have silently deleted the animation.

Run the project's type check and build afterward, then **update the "now"
column**. That is the next audit's reference line.

## 6. Record the verdict

Items closed as "checked, not a problem" go in the baseline **with evidence and
a reopen condition.** Otherwise they come back next time.

```markdown
| accent usage | 77 | not a problem | reopen if opaque bg-accent exceeds 21 |
```

Evidence is numeric. Not "looks fine" but "27 of 77 are hover-only, so resting
exposure is 50".

## When nothing is wrong

Say so briefly and stop. Remember failure mode 1.

