references/design-system.md
# The design system — build this before any screen
The single rule everything else follows from:
> **No colour, size, radius, spacing value or font stack ever appears inside a component.** Every one of them is a named token defined in one file. A complete rebrand is one file changed.
If a component contains `#DC5B42` or `padding: 18px`, the system has already failed — because the rebrand, the dark mode and the accessibility pass all become a hunt through every screen.
---
## Colour
### Name colours by ROLE, never by appearance
The most common failure in a mockup that later needs to change: `--blue-500`. When the brand becomes green, every name is a lie and every usage has to be re-read to work out what it meant.
```
--surface the page behind everything
--surface-raised cards and panels sitting on it
--surface-sunken wells, inputs, chart tracks
--ink primary text
--ink-soft labels and secondary text
--ink-faint disabled, placeholder
--accent the brand colour — the ONE thing that changes on rebrand
--accent-hover pressed and hover states
--accent-wash tint behind icons and soft badges
--rule hairlines and borders
--positive success and confirmation
--caution warnings
--critical destructive and error
```
**Semantic names survive a rebrand; literal names do not.** With this set, changing `--accent` changes the product.
### Keep the palette small
**4–6 named values plus neutrals.** More than that and the mockup stops looking designed and starts looking assembled. If a seventh colour seems necessary, it is usually a job for a shade of an existing one.
### Light and dark from day one
Not a later pass. Retrofitting dark mode means revisiting every screen, and every hard-coded value you got away with becomes visible at once.
```css
:root { --surface: #FAF7F5; --ink: #1C1A19; --accent: #DC5B42; }
@media (prefers-color-scheme: dark) {
:root { --surface: #171513; --ink: #F4F0ED; --accent: #E87059; }
}
:root[data-theme="dark"] { /* explicit override wins over the media query */ }
:root[data-theme="light"] { }
```
Support both the system preference **and** an explicit toggle, and let the explicit one win in both directions.
**Dark is not inverted light.** Three things that always need adjusting:
- **Lift the accent's lightness.** A colour that reads well on white is usually muddy on near-black.
- **Never use pure black or pure white.** `#000` on `#FFF` is harsh; near-black and near-white are calmer and look intentional.
- **Shadows barely work in dark.** Separate surfaces with a lighter raised colour and a subtle border instead of a drop shadow.
### Contrast is a functional requirement
WCAG AA — 4.5:1 for body text, 3:1 for large text and UI boundaries. **Check both themes.** For an older or low-vision audience, treat AA as the floor and aim higher on anything read at distance.
---
## Spacing
**One scale, and everything snaps to it.** A 4px base is the standard choice:
```
--space-1: 4px --space-4: 16px --space-7: 40px
--space-2: 8px --space-5: 24px --space-8: 64px
--space-3: 12px --space-6: 32px --space-9: 96px
```
**No value outside the scale.** The moment `padding: 18px` appears, the rhythm is gone and nobody can tell why the page feels slightly wrong.
**Space communicates grouping.** Related things sit closer than unrelated things, and that difference should be at least two steps of the scale or the eye will not read it. Most cramped-looking interfaces are not short of space overall — they use the *same* gap between related and unrelated elements.
---
## Radius — pick relationships, not numbers
The failure this prevents: a card at 28px, a button at 6px, an input at 12px, chosen separately at different times. Nothing is wrong individually and the whole thing looks incoherent.
**Define a scale and state the relationships:**
```
--radius-sm 6px inputs, small controls, table cells
--radius-md 12px buttons, chips, badges
--radius-lg 20px cards nested inside other cards
--radius-xl 28px top-level cards, panels, modals
--radius-full 999px pills, avatars, circular buttons
```
**The two rules that keep it coherent:**
1. **Nested corners get smaller, not bigger.** A card inside a card steps *down* the scale. A large radius inside a small one reads as a mistake even to people who cannot say why.
2. **Concentric radii should relate**: an inner radius of roughly `outer − padding` keeps the curves parallel. A 28px card with 16px padding wants an inner radius near 12px.
**Commit to a personality and hold it.** Fully round pills with 6px cards is a mixed message. Either the product is soft (large radii, pills everywhere) or it is precise (small radii, square-ish) — pick one and be consistent. If buttons are pills, chips should be pills too.
---
## Type
**Two faces, three at most:**
- **Display** — carries the personality. Used with restraint, at large sizes only.
- **Body / UI** — chosen for legibility, not character. This is most of the product.
- Optionally a **mono** for code, data, or timers where digit alignment matters.
**One scale, and every size comes from it:**
```
--text-xs 13px --text-lg 20px --text-3xl 44px
--text-sm 15px --text-xl 24px --text-4xl 56px
--text-base 17px --text-2xl 32px --text-hero 96px+
```
**Set the base from the audience, not from habit.** 16px is a floor, not a default. An older or low-vision audience wants 17–19px body and everything else scaling from it.
**Always set line height with size.** Tight for display (1.1–1.2), comfortable for body (1.5–1.6). Long body text at 1.2 is unreadable and it is the single most common type mistake.
**Numbers that update need `font-variant-numeric: tabular-nums`** — otherwise a running timer visibly jitters as digit widths change.
---
## Elevation
Pick **one** way to separate surfaces and use it consistently: shadow, border, or a background shift. Mixing all three is what makes a UI look assembled by different people.
Three levels is plenty — flat, raised, floating (modals, menus). If a fourth seems necessary, the layout is probably too deep.
Remember shadows barely read in dark mode; plan the border or surface-shift equivalent at the same time.
---
## Components — define once, use everywhere
Build these before the screens, from tokens only:
**Button** — primary, secondary, ghost, destructive; default, hover, active, focus, disabled, loading. **A button with no focus state is not finished.**
**Input** — label, field, helper text, error state, disabled. Errors say what to do next.
**Card** — the container everything sits in.
**Chip / pill** — selected and unselected. Selected must be distinguishable **without relying on colour alone**.
**Modal** — backdrop, panel, close, scrollable body, Escape to dismiss, focus trapped.
**Table row** — including the hover and clickable states, if rows open.
**Empty state** — an illustration or glyph, a line explaining what goes here, and the action that fills it. Design it once and reuse it.
**Minimum target size 44×44px**, and larger for an audience with reduced dexterity.
---
## Navigation
Pick the pattern from the shape of the product, not from fashion:
- **Sidebar** — 4–8 top-level destinations, desktop-first, and the user moves between them often. Most tools.
- **Top bar** — few destinations, or content-led and marketing-adjacent.
- **Bottom bar** — mobile, 3–5 destinations, thumb reach matters.
Whatever the pattern: **the current location must be unmistakable** (not a faint tint), destinations are named for what the user does there rather than how the system is built, and **full-screen modes are entered from a destination rather than being one.** A workout session, a video player, a canvas — these take over the screen but do not appear in the nav.
---
## Motion
**Default to less.** Extra animation is one of the strongest signals that an interface was generated rather than designed.
```
--motion-fast 120ms hover, focus, small state changes
--motion-base 200ms most transitions
--motion-slow 320ms modals, page-level changes
```
Ease-out for things entering, ease-in for things leaving. **Respect `prefers-reduced-motion` and mean it** — not a shorter animation, no animation.
One orchestrated moment lands harder than movement scattered everywhere. And during any task where the user is concentrating, motion should stop entirely.
---
## Making the brand swappable
The client will change their mind, or the same mockup will be shown to a second client. Build for it from the start:
1. **Every visual value is a token**, no exceptions.
2. **Colour tokens are semantic**, so `--accent` means "the brand colour" regardless of what colour it is.
3. **Font families are two tokens**, `--font-display` and `--font-body`, referenced nowhere else.
4. **The radius scale is one block** — softening or sharpening the whole product is five numbers.
5. **Keep a themes block**, so an alternative brand is a set of overrides rather than an edit:
```css
:root[data-brand="alt"] {
--accent: #2F6F5E;
--font-display: "Some Other Face", serif;
--radius-xl: 8px;
}
```
**Test it before you finish.** Change `--accent`, reload, and look at every screen. Anything that did not change is a hard-coded value you missed — and that is exactly the bug you are trying to prevent.
---
## The style guide page
A `/styleguide` route rendering the system from the same tokens the app uses:
- Palette swatches with token names and values, in both themes
- The type scale, each size labelled and shown in use
- The spacing and radius scales, drawn
- Every component in every state
- A light/dark toggle
Two reasons it earns its place: the client can approve or reject the **branding at token level**, which is a much cheaper conversation than arguing screen by screen — and it documents the system for whoever builds the real thing.
---
## The checklist
- [ ] No hex, px, or font stack in any component.
- [ ] Colour tokens are **semantic**, not literal.
- [ ] **Light and dark both work**, on every screen, and the toggle beats the system preference.
- [ ] Contrast meets AA in **both** themes.
- [ ] One spacing scale, and nothing off it.
- [ ] Radius scale with **stated relationships**; nested corners step down.
- [ ] Type scale with line heights; base size set from the audience.
- [ ] Tabular numerals anywhere numbers change.
- [ ] Every component has hover, focus, active and disabled.
- [ ] Empty states designed.
- [ ] Targets ≥ 44px.
- [ ] Reduced motion respected.
- [ ] **Changing `--accent` visibly rebrands every screen.**
references/review-method.md
# Building it, showing it, and feeding it back
The method behind `cs-build-ui-mockup`. Learned the expensive way on a real client.
## The problem it solves
`elicitation.md` warns that a confident plan which does not match what is in the person's head **reads exactly like a good one**. Everything else in this skill fights that with words — read it back, ask what is wrong, get a yes at the gate.
**Words are not enough.** A person will agree to a written plan they have not actually understood, because prose lets both sides fill the gaps differently and neither notices. The gap only becomes visible when someone can *look* at it.
Real run: a plan was written, agreed at the gate, expanded into PRDs and sixty tasks. It looked complete. A clickable mockup was then built from it, and within ten minutes of the owner looking at the screens it produced **eleven corrections** — including a whole screen in the wrong order, a feature nobody could find, a control that had no source for its numbers, and a list nobody could open. None of those had surfaced in any amount of written review, and several would have been expensive to fix after the build.
> **A mockup is the cheapest possible disagreement.**
## When to build one
**After the map and notes, before the PRDs.** That is the moment the plan is complete enough to draw and cheap enough to be wrong.
Skip it only when there is genuinely no interface — a CLI, a library, a background job.
The design system that the mockup is built from is a separate concern with its own rules: `design-system.md`. Build that first.
## What to build
**A static, clickable, throwaway front end.** Not the app. No backend, no real data, no auth, no camera, no API calls. Hardcoded sample values everywhere.
- **One screen per core feature**, plus the single most important flow.
- **Real copy**, not lorem ipsum. Wrong copy is one of the things you are trying to surface, and placeholder text hides it.
- **Plausible sample data.** Numbers that could be real. "Sit-to-Stand Strength · 10 minutes · Level 2" surfaces problems that "Item 1" never will.
- Enough interactivity that clicking a thing visibly does something. **A control that does not respond reads as broken, not as static**, and you will waste review time on it.
Build it in whatever is fastest to throw away. It is a communication device with a lifespan of days.
## How to run the review
Put it in front of them and **shut up**. Do not narrate, do not explain the reasoning, do not pre-defend a choice. The moment you explain a screen you have contaminated the test — you are now measuring whether your explanation is convincing, not whether the screen is.
Ask only:
- *"Show me what you'd do first."*
- *"What's confusing?"*
- *"What's missing?"*
- *"Where would you look for X?"*
**Then take the feedback literally, and separately work out what it means.** "This looks blobby" is not actionable, but the thing they are pointing at is real. "I don't know what that's showing" means a chart has no title. "Is that something I click?" means one screen is doing two jobs with no signposting.
## What a mockup reliably catches that prose does not
Every one of these came from a real review, and every one had been invisible in a written plan that both parties had signed off:
| What surfaced | The general lesson |
|---|---|
| The screen asked two questions before showing anything useful | **Screen ORDER is a decision, and prose does not encode it.** A note listing features says nothing about what is at the top. |
| A summary line duplicated what the graphic already said | Redundancy is invisible in a list and obvious on a screen. |
| A control had no source for its numbers | **"The user picks a level" hides the question of where the level's VALUES come from.** Prose accepts an unfinished thought; a form has to be filled in. |
| A list of items could not be opened | **A list nobody can open is a list nobody can trust.** Detail views get planned as "obviously"; obviously never gets built. |
| A fixed set of options had no escape hatch | **Every enumerated choice needs an "other".** A list of five equipment types dies on contact with a real user. |
| An important secondary action was at the bottom of a long page | Position IS priority, and a note cannot express position. |
| A screen in the nav had nothing behind it | Prose lists it as a feature; the mockup shows it is empty. |
| Status was scattered across five floating elements | **"Show the round, the reps and the timer" reads fine as a sentence and fails as a screen.** |
| Everything on a bar had equal weight | **A written plan cannot express a focal point.** Prose has no hierarchy; a screen is nothing but hierarchy. |
| A real feature read as invented | If a feature's purpose is not legible on screen, it will be cut by whoever reviews it — including the person who asked for it. |
Notice the pattern: **almost all of it is about hierarchy, order, position, and the completeness of a choice.** Those are exactly the properties a bulleted list cannot represent, which is why no amount of re-reading the plan finds them.
## Feed it straight back into the plan
The review output is not a UI to-do list — it is **plan corrections**, and they belong in CodeSpring where the build will read them:
1. Write each decision into the relevant feature's **note**, with the reason. *"Order changed: the workout goes first because asking two questions before showing anything is two decisions before she has seen anything."* The reason is what stops it being reverted by the next person.
2. Add a **DESIGN REVIEW** block to every affected **task**, so whoever builds that screen sees the correction and does not rediscover the original mistake.
3. **Add tasks for what the review revealed was missing.** A detail view, a defaults table, an empty screen that was only ever a nav item.
4. Add sub-features for anything genuinely new.
5. Keep the mockup and **name it in the notes as the agreed UI reference.** It is now worth more than any written description of the same screens.
## Expect more than one round
The first revision will not be right either. The second review is cheaper and sharper because the obvious problems are gone and the person is now looking at hierarchy rather than content.
**Two rounds is normal. Three is fine.** It is still radically cheaper than discovering any of it after the build. Stop when the feedback moves from "this is wrong" to "I'd prefer" — at that point you have the understanding you came for, and the rest is taste that can be settled during the build.
## What good looks like
- A clickable mockup existed **before** the PRDs were generated.
- It used real copy and plausible data, and its controls visibly responded.
- It was shown **without narration**, and the questions asked were open.
- Every piece of feedback became a **note correction with its reason**, a **task addition**, or both — not a private to-do list.
- The mockup is named in the notes as the agreed UI reference.
- At least one thing surfaced that no amount of reading the plan had found. **If nothing did, the mockup was too vague to be useful.**
SKILL.md
---
name: cs-build-ui-mockup
description: >
Turn a CodeSpring plan into a clickable local mockup the client can actually
look at — one screen per core feature, real copy, plausible data, a proper
token-driven design system with light and dark mode, and a style guide page
showing the branding. Then run the review that surfaces what the written plan
could not: screen order, hierarchy, position, and choices that turn out to be
incomplete. Feeds every correction back into the CodeSpring notes and tasks.
Runs AFTER the map and notes exist and BEFORE the PRDs are generated.
Triggers, "make a mockup", "mock up the UI", "show me what it looks like",
"build a prototype", "design the screens", "what would this app look like",
"create a UI mockup for this plan", "I want to show the client something".
allowed-tools: Bash(codespring:*) Bash(npx @codespring-app/cli:*) Bash(bash:*) Bash(node:*) Bash(npm:*) Bash(npx:*) Bash(git:*) Bash(grep:*) Bash(rg:*) WebSearch WebFetch
metadata:
author: codespring
version: "0.1"
---
# Build a clickable mockup from the plan
**A written plan cannot express a focal point.** It cannot express screen order, position, hierarchy, or whether a set of choices is complete. Those are exactly the things that go wrong, and no amount of re-reading the notes will find them — the client agrees to the words and then sees the screens and says something quite different.
This skill turns the plan into something they can look at, runs the review that surfaces the gap, and writes what it finds back into CodeSpring.
**Where it sits in the chain:**
```
cs-build-plan-app or cs-build-import-codebase (map + notes exist)
│
▼
cs-build-ui-mockup ← you are here
│ build it, show it, feed corrections back
▼
cs-build-create-prd → cs-build-create-tasks → cs-build-feature
```
**Run it before the PRDs.** A PRD generated from a plan nobody has seen just encodes the misunderstanding in more detail and makes it more expensive to undo.
Read: `codespring` skill `references/project-state.md`, `references/pitfalls.md`, and this skill's own references —
| Reference | Read it when |
|---|---|
| `references/design-system.md` | **Before writing any CSS.** The token architecture, the scales, the radius relationships, brand-swapping, and light/dark. |
| `references/review-method.md` | Before showing it to anyone. How to run the review without contaminating it, what mockups reliably catch, and how corrections get written back. |
**Also load Anthropic's `frontend-design` skill if the runtime supports it** (`https://github.com/anthropics/skills/blob/main/skills/frontend-design/SKILL.md`). It is the method for making the thing distinctive rather than templated; `design-system.md` here is the structure it hangs on. If skills cannot be installed in this environment, read that URL and follow it directly.
---
## 0. Check the plan is ready
```bash
bash ../codespring/scripts/fetch-project.sh --out /tmp/cs-state
node ../codespring/scripts/state.mjs /tmp/cs-state
```
You need **core features with notes**. Without notes there is nothing to draw — the note is where the screen's content, states and rules live.
- **No map** → wrong skill. `cs-build-plan-app` (no code) or `cs-build-import-codebase` (existing code) first.
- **Map but thin or missing notes** → say so and fix the notes first. A mockup built from card titles alone invents everything and tests nothing.
- **PRDs already generated** → still worth doing, but say plainly that corrections will mean regenerating them.
## 1. Get the brief straight before drawing anything
From the notes and from the person, settle these. They are the inputs to every visual decision:
- **Who uses it**, and what that constrains — age, ability, environment, device, how much patience they have.
- **The single job of the main screen.**
- **The one moment the product will be remembered by.** This is where the boldness gets spent; everything else stays quiet.
- **Any brand that already exists** — colours, type, logo, an existing product to match.
- **Any reference the client has supplied.** If they have pinned a direction, **follow it exactly** — their words win over any default you would otherwise reach for.
- **Light or dark or both**, and which is the default.
**If a reference image was supplied, extract from it explicitly** and write the extraction down: palette as named hex values, radius scale, type pairing, spacing rhythm, and the two or three structural patterns worth stealing. Then say what you are deliberately *not* taking from it, and why. A reference is evidence of what they liked, not a specification to copy wholesale — the reference's audience is often not their audience.
## 2. Choose the smallest stack that throws away cleanly
This is a **communication device with a lifespan of days**. It is not the app and it must never become the app by accident.
Default: whatever gets a styled, routed, clickable page up fastest in the target platform's own idiom — usually a scaffolded web app with a utility CSS layer. Match the eventual stack where that is free, but **never let stack fidelity slow the mockup down.** A web mockup for a native app is fine and normal; the point is the screens.
**No backend. No database. No auth. No real API calls.** Hardcoded sample data throughout.
Put it in its own directory, clearly named `mockup/`, and say in one line that it is throwaway.
## 3. Build the design system FIRST
Do not write a screen before the tokens exist. `references/design-system.md` is the spec; the short version:
- **Every value lives in one place.** No hex, no pixel value, no radius, no font stack appears in a component. Ever. A whole rebrand must be one file.
- **Light and dark are both built from day one**, not retrofitted. Retrofitting dark mode means revisiting every screen.
- **One spacing scale, one type scale, one radius scale.** Radii are chosen as a deliberate relationship — see the reference; the common failure is a card at 28, a button at 6 and an input at 12 for no reason anyone can state.
- **Colour is semantic, not literal.** `--surface`, `--ink`, `--accent` — never `--blue`. Semantic names are what make a brand swap possible.
Then run the **two-pass process** from Anthropic's frontend-design skill: plan the token set (4–6 named colours, display + body faces, a layout concept, and the one signature element), **critique that plan against the brief**, revise anything that reads as a generic default, and only then write code.
## 4. Build the screens
**One screen per core feature, plus the single most important flow.** Do not build every sub-feature — build enough that the shape of the product is legible.
Non-negotiables, because each one has cost a real review:
- **Real copy.** Never lorem ipsum. Wrong wording is one of the things you are trying to surface, and placeholder text hides it completely.
- **Plausible sample data.** Real-looking names, real-looking numbers. "Sit-to-Stand Strength · 10 minutes · Level 2" surfaces problems that "Item 1" never will.
- **Controls must visibly respond.** A chip that does nothing when clicked reads as **broken**, not as static, and you will lose review time to it. Hardcoded state that moves on click is enough.
- **Draw the empty and first-run states.** They are the most-skipped screens and the ones that most often read as "this is broken" to a non-technical user.
- **Every list item that has detail behind it must open.** A list you cannot open is a list nobody can trust, and "you'd click into it" is exactly the kind of assumption a mockup exists to expose.
- **Every enumerated choice needs its escape hatch** — an "Other" or "Custom" option — unless the set is genuinely closed. Fixed lists die on contact with real users.
## 5. Add a style guide page
A `/styleguide` route showing the design system as an artefact the client can react to: the palette with names and values, the type scale in use, the spacing and radius scales, buttons and inputs in every state, cards, and the light/dark toggle.
This does real work. It gives the client something concrete to approve or reject **at the token level**, which is a far cheaper conversation than arguing about it one screen at a time. It also documents the system for whoever builds the real thing.
## 6. Check it yourself before showing anyone
- The build passes and every route loads.
- **Look at it.** Screenshot each screen and actually read them. Do not ship a mockup you have only verified with HTTP 200s.
- Light and dark both work on every screen.
- Nothing framework-branded is visible — dev badges, error overlays, placeholder favicons. A non-technical client reads any of that as "broken".
- The quality floor: keyboard focus visible, sensible heading order, reduced motion respected, no horizontal scroll.
- **Chanel's rule: find the one thing to remove.**
## 7. Run the review
Full method in `references/review-method.md`. The core of it:
**Put it in front of them and shut up.** Do not narrate, do not explain your reasoning, do not pre-defend a choice. The moment you explain a screen you are measuring whether your explanation is convincing, not whether the screen is.
Ask only open questions: *show me what you'd do first*, *what's confusing*, *what's missing*, *where would you look for X*.
**Take the feedback literally and work out separately what it means.** "This looks blobby" is not actionable but the thing they are pointing at is real. "I don't know what that's showing" means a chart has no title. "Is that something I click?" means one screen is doing two jobs with no signposting.
**Expect two rounds.** Stop when the feedback moves from "this is wrong" to "I'd prefer" — at that point you have the understanding you came for and the rest is taste that can be settled during the build.
## 8. Write the corrections back into CodeSpring
**This is the step that makes the skill worth running.** The review output is not a UI to-do list — it is plan corrections, and they belong where the build will read them.
1. **The reason goes in the feature's note.** Not just the decision — the reason. *"The workout goes first, because asking two questions before showing anything is two decisions before she has seen anything."* The reason is what stops the next person reverting it.
2. **A `DESIGN REVIEW` block goes on every affected task**, so whoever builds that screen sees the correction rather than rediscovering the original mistake.
3. **Anything the review revealed as missing becomes a new task**, and a new sub-feature if it is genuinely a new thing the user does.
4. **Name the mockup in the notes as the agreed UI reference**, with how to run it. It is now worth more than any written description of the same screens.
```bash
codespring mindmap note <coreFeatureId> --title "How it works — X" --text "$(cat note.txt)"
codespring task update <taskId> --description "$(cat task.txt)"
codespring task create --title "0.x.y — ..." --description "..." --feature <id> --priority high
```
## 9. Hand off
**→ `cs-build-create-prd`**, now that the plan reflects something the client has actually seen.
Then `cs-build-create-tasks`, then `cs-build-feature`.
Tell them plainly what they have: the mockup and how to run it, the style guide, what changed in the plan because of the review, and what happens next.
---
## What good looks like
- The mockup existed **before** the PRDs.
- **The design system was built first**, all values live in one place, and light and dark both work.
- A `/styleguide` route exists, so branding is a token-level conversation.
- Real copy, plausible data, controls that respond, and empty states drawn.
- Every list with detail behind it opens; every enumerated choice has an escape hatch.
- It was shown **without narration**, with open questions.
- **Every correction became a note reason and a task block** — not a private to-do list.
- The mockup is named in the notes as the agreed UI reference.
- **At least one thing surfaced that no amount of reading the plan had found.** If nothing did, the mockup was too vague to be useful.