Visually verify implemented UI against docs/ux/DESIGN.md tokens and EXPERIENCE.md states by building and observing the running app or site. Called by /dev-story for UI stories; directly invocable as /design-verify to check the current working tree.
Installs into .claude/skills of the current project.
Are you the author of Design Verify?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/rterakedis-design-verify)
---
name: design-verify
description: Visually verify implemented UI against docs/ux/DESIGN.md tokens and EXPERIENCE.md states by building and observing the running app or site. Called by /dev-story for UI stories; directly invocable as /design-verify to check the current working tree.
---
# Design Verify Skill
**Goal:** Confirm that user-visible UI changes actually match the design contract — by rendering them, not by re-reading the code. Code review catches wrong logic; this catches wrong *pixels*: off-token colors, missing dark mode, broken states, truncated type.
**When:** Invoked inline by `/dev-story` after DoD passes for any story that changed user-visible UI, or directly via `/design-verify`. Skip entirely (report "no user-visible surface changed") for pure backend/refactor diffs.
**Verify reachability, not presence.** A string, control, or affordance existing in source is not evidence it can reach the screen — drive the app or dump the hierarchy. When an element can't be addressed in a UI test, dump the tree before blaming the tooling; the element may not render at all. Canonical: `dev-story` verification step.
**Requires:** `docs/ux/DESIGN.md` and/or `docs/ux/EXPERIENCE.md`. If neither exists, fall back to the platform guardrails checklist only (`docs/setup/swift/` or `docs/setup/web/`).
---
## Step 1 — Scope
1. Identify changed surfaces: from the story's File List (or `git diff --name-only` when standalone), list the screens/pages/components a user can see.
2. From `docs/ux/EXPERIENCE.md`, pull the required states for each changed surface (empty / loading / error / offline, per the State Patterns section).
3. From `docs/ux/DESIGN.md` frontmatter, pull the tokens the changed surfaces consume.
4. If the story has a `### Design Contract` section in Dev Notes, that is the checklist source — don't re-derive.
## Step 2 — Render
Pick the first option that works; degrade gracefully. Never block the story on missing tooling.
**Apple platform — use `scripts/sim.sh`** (see `docs/setup/swift/simulator.md`). Do not hand-roll `xcrun` calls: device names change every Xcode release, `simctl boot` errors when the device is already booted, and a native screenshot is ~2.9 MB to read back. The harness handles all of that.
```bash
scripts/sim.sh install # once per code change
scripts/sim.sh shots {surface} --route {route} --seed empty # then edge / heavy
```
One `shots` run captures the full matrix — light/dark × default/accessibility-XL × iPhone/iPad — into `.leanwheel/sim/shots/{timestamp}/`, downscaled for cheap reading. Read those PNGs back directly.
- **Reach the surface by its deep-link route** (`--route`), never by tapping. If the changed surface has no route, that is itself a MEDIUM finding — record it and screenshot what is reachable.
- **Drive the states the Design Contract requires** via `--seed` (`empty` / `edge` / `heavy`), rather than verifying whatever state the simulator happens to hold.
- If every screenshot shows a permission alert, the device has a wedged system alert: re-run with `--fresh`.
- No `sim.sh` (older project): run `scripts/sim.sh doctor` if the script exists, otherwise fall back to the manual checklist below rather than improvising `xcrun` incantations.
The **accessibility-XL column is not optional** — truncation, clipped labels, and controls pushed off-screen appear there and nowhere else.
**Web / SSG:**
```bash
# Astro: npm run dev | Hugo: hugo server
# then screenshot changed pages at two widths (mobile ~390px, desktop ~1280px)
```
Use whatever browser/screenshot tooling the session has (browser MCP, preview tool, Playwright if installed: `npx playwright screenshot --viewport-size=390,844 {url} /tmp/dv-mobile.png`). Capture both `prefers-color-scheme` values if the site supports dark mode.
If the site uses a custom font, check for visible font swap: load a changed page with cache disabled (or first visit in a fresh browser context) and compare a screenshot at first paint vs. after load — text changing glyphs or shifting position is a HIGH finding (guidance requires `font-display: optional` + metric-matched fallback). If tooling can't capture first paint, add "hard-reload with cache disabled and watch body text for flash/reflow" to the manual checklist.
**Nothing available:** Don't fake it. Write a **MANUAL VERIFICATION** checklist instead (concrete tap/click paths per surface and state, derived from the ACs) and mark the verification `manual-required`.
## Step 3 — Compare
For each captured surface, check against the contract. Read the screenshots (you can view images) and answer:
| Check | Source |
|---|---|
| Colors match tokens (no near-miss hex drift); both appearances correct | DESIGN.md `colors` |
| Spacing/radii consistent with scale (no visibly off-grid gaps) | DESIGN.md `spacing` / `rounded` |
| Type roles and hierarchy as specced; no truncation at large type / 200% zoom | DESIGN.md `typography` |
| Required states render (empty / loading / error) — drive them where feasible | EXPERIENCE.md State Patterns |
| Layout holds at both sizes (mobile/desktop or iPhone/iPad as in scope) | EXPERIENCE.md platform section |
| Components reuse the established inventory, not near-duplicates | `docs/ux/components-built.md` |
| Platform checklist items visible in the capture (safe areas, tap targets / focus visibility) | HIG checklist / web guardrails |
States that can't be driven from the UI (e.g., a server error) get a one-line note in the manual checklist rather than silent omission.
## Step 4 — Record and Hand Back
Write results into the story file under `### Design Verification` (create if missing):
```markdown
### Design Verification
- Surfaces verified: {list} ({N} screenshots: light/dark × sizes)
- [x] {check that passed}
- [ ] [HIGH/MEDIUM/LOW] {mismatch} — expected {token/state from contract}, observed {what rendered}
- Manual verification required: {none | list of steps}
```
Severity: `HIGH` = wrong/missing state, unreadable text, broken layout, missing dark mode on a load-bearing surface. `MEDIUM` = off-token value, spacing drift. `LOW` = polish nits.
**When invoked from `/dev-story`:** leave mismatches in `### Design Verification` — the independent reviewer (code-review Pass E) triages them into `patch`, `fix-now`, or `decision-needed` findings like any other. The story does not reach `done` with an unresolved HIGH design finding.
**When standalone:** report the table, fix `patch`-grade mismatches on request.