UI Consistency Guard
An agent building a new screen has no picture of the app it is adding to, so it
invents: a new shade of blue, its own spacing, a button it wrote inline instead
of the one the app already ships. Nothing is wrong locally; the app drifts
anyway.
One habit fixes it: look at the existing app first, build from what is already
there, and when you must invent, say so.
Modes
| Situation |
Mode |
Read |
No UI-PATTERNS.md for this surface yet |
A — Learn |
references/learn.md |
| Writing or changing UI |
B — Build |
references/build.md |
| Reviewing a diff, PR, or existing screen for drift |
C — Review |
references/review.md |
Read only the reference for the mode you are in. Mode A runs once per
surface; Mode B is the common case.
Routing: if the task writes UI → check for UI-PATTERNS.md (repo root, or
the nearest one up the tree from the files you are touching). Missing or its
generated_at commit is far behind HEAD → Mode A first, then Mode B. Present and
current → straight to Mode B.
Running the scripts
Two zero-dependency Node scripts (Node 16+) ship with this skill:
scripts/scan-tokens.mjs (Mode A) and scripts/check-drift.mjs (Modes B/C).
Paths are relative to this skill's directory, not the user's repo — run them
from the user's repo root with an absolute or skill-relative path, e.g.
node <skill-dir>/scripts/check-drift.mjs. Both take --help. If Node is
unavailable, fall back to reading and grepping by hand using the same rules.
Fast path
For a genuinely small change — one style value, one class, one prop — skip the
planning ceremony. Read the pattern file's token tables, use a token, done. The
full Mode B sequence is for new components and screens.
Non-negotiables
- Reuse before you create. An existing component that fits wins over a new
one, every time. A new component is a last resort that must be built from
existing tokens and reported as new.
- Never hard-code a design value that already exists as a token. Colours,
spacing, radii, type sizes come from the app's source of truth.
- Never block. If nothing existing fits, derive from the nearest existing
value, use it, and flag it as
NEW: in your report. Do not stop and wait for
a human unless the decision is brand-level (a new brand colour, a new type
family) — see the decision ladder in references/build.md.
- Verify before you trust.
UI-PATTERNS.md is a cache, not truth. Confirm a
token still resolves in source before you use it. If the source moved, fix the
pattern file in the same change.
- Report drift instead of hiding it. Every UI change ends with what was
reused and what is new.
When NOT to apply this
Consistency is the default, not a religion. Do not force it when:
- The user explicitly asks for something different — a redesign, a new visual
direction, an A/B variant. Say once that it diverges from the current system,
then build what was asked.
- Accessibility requires it. A contrast, hit-target, or focus-visibility fix
outranks the token. Fix it, and note that the token itself may need to change.
- The surface is deliberately separate — a marketing/landing page, an embedded
partner widget, an internal debug screen. Match that surface's patterns.
- The repo already enforces a real system — a tokens package, Tailwind
config, MUI/Chakra theme, shadcn registry. Defer to it and point at it. Do not
build a competing summary;
UI-PATTERNS.md becomes a thin index over it.
- Greenfield. If there is almost no existing UI, there is no system to
conform to. Say so plainly rather than extrapolating one from three files.
The one rule
Before you write a raw colour, a spacing number, or a brand-new component, ask:
does this already exist in the app? Almost always it does. Reaching for the
existing thing instead of inventing a slightly different new thing is the entire
job.
1---2name: ui-consistency-guard3description: Use when generating, editing, or reviewing user-facing UI in an existing codebase — a new screen, page, component, modal, form, list, card, empty/loading/error state, or any visual change — in React, React Native, Vue, Svelte, Angular, Flutter, SwiftUI, or plain HTML/CSS. Makes new UI reuse the app's existing colours, spacing, radii, type, and components instead of inventing new ones, and can review a diff for visual drift. Trigger even when the user never says "consistency", "design system", or "style". Do NOT trigger for copy/text-only edits, logic or data changes with no visual output, build/config files, or tests.4license: MIT5---67# UI Consistency Guard89An agent building a new screen has no picture of the app it is adding to, so it10invents: a new shade of blue, its own spacing, a button it wrote inline instead11of the one the app already ships. Nothing is wrong locally; the app drifts12anyway.1314One habit fixes it: **look at the existing app first, build from what is already15there, and when you must invent, say so.**1617## Modes1819| Situation | Mode | Read |20|---|---|---|21| No `UI-PATTERNS.md` for this surface yet | **A — Learn** | `references/learn.md` |22| Writing or changing UI | **B — Build** | `references/build.md` |23| Reviewing a diff, PR, or existing screen for drift | **C — Review** | `references/review.md` |2425Read **only** the reference for the mode you are in. Mode A runs once per26surface; Mode B is the common case.2728**Routing:** if the task writes UI → check for `UI-PATTERNS.md` (repo root, or29the nearest one up the tree from the files you are touching). Missing or its30`generated_at` commit is far behind HEAD → Mode A first, then Mode B. Present and31current → straight to Mode B.3233## Running the scripts3435Two zero-dependency Node scripts (Node 16+) ship with this skill:36`scripts/scan-tokens.mjs` (Mode A) and `scripts/check-drift.mjs` (Modes B/C).3738Paths are **relative to this skill's directory**, not the user's repo — run them39from the user's repo root with an absolute or skill-relative path, e.g.40`node <skill-dir>/scripts/check-drift.mjs`. Both take `--help`. If Node is41unavailable, fall back to reading and grepping by hand using the same rules.4243## Fast path4445For a genuinely small change — one style value, one class, one prop — skip the46planning ceremony. Read the pattern file's token tables, use a token, done. The47full Mode B sequence is for new components and screens.4849## Non-negotiables50511. **Reuse before you create.** An existing component that fits wins over a new52 one, every time. A new component is a last resort that must be built from53 existing tokens and reported as new.542. **Never hard-code a design value that already exists as a token.** Colours,55 spacing, radii, type sizes come from the app's source of truth.563. **Never block.** If nothing existing fits, derive from the nearest existing57 value, use it, and flag it as `NEW:` in your report. Do not stop and wait for58 a human unless the decision is brand-level (a new brand colour, a new type59 family) — see the decision ladder in `references/build.md`.604. **Verify before you trust.** `UI-PATTERNS.md` is a cache, not truth. Confirm a61 token still resolves in source before you use it. If the source moved, fix the62 pattern file in the same change.635. **Report drift instead of hiding it.** Every UI change ends with what was64 reused and what is new.6566## When NOT to apply this6768Consistency is the default, not a religion. Do not force it when:6970- **The user explicitly asks for something different** — a redesign, a new visual71 direction, an A/B variant. Say once that it diverges from the current system,72 then build what was asked.73- **Accessibility requires it.** A contrast, hit-target, or focus-visibility fix74 outranks the token. Fix it, and note that the token itself may need to change.75- **The surface is deliberately separate** — a marketing/landing page, an embedded76 partner widget, an internal debug screen. Match *that* surface's patterns.77- **The repo already enforces a real system** — a tokens package, Tailwind78 config, MUI/Chakra theme, shadcn registry. Defer to it and point at it. Do not79 build a competing summary; `UI-PATTERNS.md` becomes a thin index over it.80- **Greenfield.** If there is almost no existing UI, there is no system to81 conform to. Say so plainly rather than extrapolating one from three files.8283## The one rule8485Before you write a raw colour, a spacing number, or a brand-new component, ask:86*does this already exist in the app?* Almost always it does. Reaching for the87existing thing instead of inventing a slightly different new thing is the entire88job.