Build a systematic design system
frontend-design decides how the product should look; this pack decides how that look is
structured so it scales. A design system is the disciplined layer underneath the aesthetic:
a three-tier token architecture, component specs that cover every interactive state,
and a deliberate match between the product's needs and the system it gets. Get this right and
every later surface reuses tokens instead of re-inventing styling per component.
Keep SKILL.md lean — the depth lives in the references; load the one you need on demand:
| Topic |
Reference |
| Three-layer token architecture (primitive → semantic → component), dark mode, naming |
references/token-architecture.md |
| Component specs — variants, sizes, full state matrices for button/input/card/etc. |
references/component-specs.md |
| Interactive states + variant patterns — state priority, focus rings, error/loading, a11y |
references/states-and-variants.md |
The token architecture in one breath
Three layers, each referencing the one below — never skip a layer:
/* PRIMITIVE — raw values, no meaning. Change rarely. */
--blue-600: oklch(55% 0.20 264);
/* SEMANTIC — purpose aliases. This is the theme-switch seam. */
--color-primary: var(--blue-600);
/* COMPONENT — per-component knobs. Change freely. */
--button-bg: var(--color-primary);
Why it matters: theming happens at the semantic layer (override --color-primary, every
component follows). Per-component tweaks happen at the component layer without disturbing
anyone else. Components reference component or semantic tokens — never a primitive
directly, and never a raw hex/px. See references/token-architecture.md.
House idiom: Gaffer uses oklch for colour and clamp() for fluid type at the
primitive layer (perceptual uniformity, predictable contrast). Adapt the references' hex
examples to oklch to stay consistent with brand and frontend-design.
Recommend a tailored design system
When the ask is open-ended ("what should this product use?"), don't reach for a generic kit.
Reason from the product to a specific system across four axes:
- Pattern / structure — content-first marketing site, data-dense dashboard/admin, a
transactional flow, or a component-library/SaaS surface. This sets density, the spacing
rhythm, and how much the grid is allowed to break.
- Style direction — defer to
brand if one exists; otherwise pick a real one
(editorial, Swiss, neo-brutalism, glassmorphism-with-depth, light/dark luxury, bento) and
say why it fits this product. Never "clean minimal".
- Palette posture — how many primitives, which semantic roles (primary + secondary +
muted + the four status colours), and whether dark mode is a real requirement or a habit.
Use the 60/30/10 dominant/secondary/accent ratio as a sanity check, not a law.
- A11y + performance posture — contrast floors (4.5:1 text, 3:1 large/UI), visible focus,
prefers-reduced-motion, plus the CWV/bundle budgets from frontend-design. State these
as targets up front so they constrain the system, not get bolted on after.
Output the recommendation as a short rationale + the token + component scaffold it implies —
then hand off to frontend-design for the visual execution and brand for the identity.
Component spec discipline
A component isn't "done" until every state is specified, not just the default. For each
component define variants (default/secondary/outline/ghost/destructive…), sizes
(sm/default/lg with explicit height + padding + font), and the full state matrix:
| State |
Trigger |
Treatment |
| default |
— |
base |
| hover |
pointer over |
one step darker/raised |
| focus |
keyboard/click |
visible focus ring (never outline:none alone) |
| active |
pointer down |
darkest |
| disabled |
disabled/aria-disabled |
muted + not-allowed, opacity ~0.5 |
| loading |
async |
aria-busy, spinner, reduced opacity, no pointer events |
| error |
invalid |
error border + ring + message, never colour alone |
State priority when several apply: disabled > loading > active > focus > hover > default.
The full matrices for button/input/card/badge/alert/dialog/table live in
references/component-specs.md; the cross-component state + variant rules in
references/states-and-variants.md.
Steps
- Read the lore + existing tokens first.
search_lore (Memory MCP) for the design
system and any token ADRs; inspect tokens.css/theme files and a sibling component. If a
token system or brand already exists, extend it in its idiom — do not introduce a
competing one.
- Establish the three layers (or audit the existing set against them). Primitives as raw
oklch/clamp() values; semantic aliases for every role; component tokens per component.
Add a .dark block that overrides semantic tokens only.
- Refuse primitive/hardcoded leaks in components. Components consume semantic/component
tokens exclusively — no raw hex, no
px font sizes, no primitive references.
- Specify components fully — variants, sizes, and the complete state matrix above, with
accessible focus and ARIA states. An unspecified state is a bug waiting to happen.
- If asked to recommend a system, run the four-axis reasoning above and output a
rationale + scaffold; hand visual execution to
frontend-design, identity to brand.
- Verify + evidence. Run the repo's tests + lint; for token work, grep components for
raw hex /
px font-size leaks. Record test_output via record-evidence and submit for
review — never self-approve.
Build / Test
- Run the repo's configured test + lint; type-check (
tsc --noEmit) for TS surfaces.
- Token-compliance check: grep component styles for raw hex and
px font sizes — they should
reference tokens, not literals.
- Verify focus is visible on every interactive component and that
.dark only overrides
semantic tokens (not primitives or component tokens).
- The DoD is verified by the repo's configured commands — record the output as evidence.
Rules
- Three layers, no skipping: components reference semantic/component tokens, never a
primitive or a raw hex/
px.
- Theme at the semantic layer; tweak at the component layer; touch primitives rarely.
- Every component spec covers the full state matrix with a visible, accessible focus ring.
- Recommend a specific system from product → pattern + style + palette + a11y/perf — defer
to
brand for identity and frontend-design for visual execution; never duplicate them.
oklch colour + clamp() type at the primitive layer to stay consistent with the house packs.
Capture lore
A repo's token architecture — the layer split, the naming convention, the dark-mode seam, or a "never reference a primitive in a component" rule — is exactly the fact the next agent needs before they start. That kind of fact is lore. Capture it via the lore-capture
protocol in your brief (CLAUDE.factory.md, step 11 "Memory contribution"):
call the Memory MCP suggest_lore once at the close of your work — reusable
conventions, gotchas, decisions, and boundaries only, never per-ticket trivia.
1---2name: design-system3description: Use when a ticket needs a *systematic* token + component foundation for a frontend — the three-layer token architecture (primitive → semantic → component), component specs with full state coverage, or a recommendation for which design system a product should have. Invoke for "set up design tokens", "define the component system", "we keep hardcoding colours — fix it", "what design system should this product use", or as the structural backbone that `frontend-design` and `brand` build distinctive UI on top of.4---56# Build a systematic design system78`frontend-design` decides *how the product should look*; this pack decides *how that look is9structured so it scales*. A design system is the disciplined layer underneath the aesthetic:10a **three-tier token architecture**, **component specs that cover every interactive state**,11and a deliberate match between the product's needs and the system it gets. Get this right and12every later surface reuses tokens instead of re-inventing styling per component.1314Keep `SKILL.md` lean — the depth lives in the references; load the one you need on demand:1516| Topic | Reference |17|-------|-----------|18| Three-layer token architecture (primitive → semantic → component), dark mode, naming | `references/token-architecture.md` |19| Component specs — variants, sizes, full state matrices for button/input/card/etc. | `references/component-specs.md` |20| Interactive states + variant patterns — state priority, focus rings, error/loading, a11y | `references/states-and-variants.md` |2122## The token architecture in one breath2324Three layers, each referencing the one below — never skip a layer:2526```css27/* PRIMITIVE — raw values, no meaning. Change rarely. */28--blue-600: oklch(55% 0.20 264);29/* SEMANTIC — purpose aliases. This is the theme-switch seam. */30--color-primary: var(--blue-600);31/* COMPONENT — per-component knobs. Change freely. */32--button-bg: var(--color-primary);33```3435Why it matters: **theming happens at the semantic layer** (override `--color-primary`, every36component follows). **Per-component tweaks happen at the component layer** without disturbing37anyone else. Components reference *component or semantic* tokens — **never a primitive38directly**, and **never a raw hex/`px`**. See `references/token-architecture.md`.3940> House idiom: Gaffer uses **`oklch`** for colour and **`clamp()`** for fluid type at the41> primitive layer (perceptual uniformity, predictable contrast). Adapt the references' hex42> examples to `oklch` to stay consistent with `brand` and `frontend-design`.4344## Recommend a tailored design system4546When the ask is open-ended ("what should this product use?"), don't reach for a generic kit.47Reason from the product to a *specific* system across four axes:48491. **Pattern / structure** — content-first marketing site, data-dense dashboard/admin, a50 transactional flow, or a component-library/SaaS surface. This sets density, the spacing51 rhythm, and how much the grid is allowed to break.522. **Style direction** — defer to `brand` if one exists; otherwise pick a real one53 (editorial, Swiss, neo-brutalism, glassmorphism-with-depth, light/dark luxury, bento) and54 say *why it fits this product*. Never "clean minimal".553. **Palette posture** — how many primitives, which semantic roles (primary + secondary +56 muted + the four status colours), and whether dark mode is a real requirement or a habit.57 Use the 60/30/10 dominant/secondary/accent ratio as a sanity check, not a law.584. **A11y + performance posture** — contrast floors (4.5:1 text, 3:1 large/UI), visible focus,59 `prefers-reduced-motion`, plus the CWV/bundle budgets from `frontend-design`. State these60 as targets up front so they constrain the system, not get bolted on after.6162Output the recommendation as a short rationale + the token + component scaffold it implies —63then hand off to `frontend-design` for the visual execution and `brand` for the identity.6465## Component spec discipline6667A component isn't "done" until every state is specified, not just the default. For each68component define **variants** (default/secondary/outline/ghost/destructive…), **sizes**69(sm/default/lg with explicit height + padding + font), and the **full state matrix**:7071| State | Trigger | Treatment |72|-------|---------|-----------|73| default | — | base |74| hover | pointer over | one step darker/raised |75| focus | keyboard/click | **visible** focus ring (never `outline:none` alone) |76| active | pointer down | darkest |77| disabled | `disabled`/`aria-disabled` | muted + `not-allowed`, opacity ~0.5 |78| loading | async | `aria-busy`, spinner, reduced opacity, no pointer events |79| error | invalid | error border + ring + message, never colour alone |8081State **priority** when several apply: disabled > loading > active > focus > hover > default.82The full matrices for button/input/card/badge/alert/dialog/table live in83`references/component-specs.md`; the cross-component state + variant rules in84`references/states-and-variants.md`.8586## Steps87881. **Read the lore + existing tokens first.** `search_lore` (Memory MCP) for the design89 system and any token ADRs; inspect `tokens.css`/theme files and a sibling component. If a90 token system or `brand` already exists, **extend it in its idiom** — do not introduce a91 competing one.922. **Establish the three layers** (or audit the existing set against them). Primitives as raw93 `oklch`/`clamp()` values; semantic aliases for every role; component tokens per component.94 Add a `.dark` block that overrides **semantic** tokens only.953. **Refuse primitive/hardcoded leaks in components.** Components consume semantic/component96 tokens exclusively — no raw hex, no `px` font sizes, no primitive references.974. **Specify components fully** — variants, sizes, and the complete state matrix above, with98 accessible focus and ARIA states. An unspecified state is a bug waiting to happen.995. **If asked to recommend a system**, run the four-axis reasoning above and output a100 rationale + scaffold; hand visual execution to `frontend-design`, identity to `brand`.1016. **Verify + evidence.** Run the repo's tests + lint; for token work, grep components for102 raw hex / `px` font-size leaks. Record `test_output` via `record-evidence` and submit for103 review — never self-approve.104105## Build / Test106107- Run the repo's configured test + lint; type-check (`tsc --noEmit`) for TS surfaces.108- Token-compliance check: grep component styles for raw hex and `px` font sizes — they should109 reference tokens, not literals.110- Verify focus is visible on every interactive component and that `.dark` only overrides111 semantic tokens (not primitives or component tokens).112- The DoD is verified by the repo's configured commands — record the output as evidence.113114## Rules115116- Three layers, no skipping: components reference semantic/component tokens, **never** a117 primitive or a raw hex/`px`.118- Theme at the **semantic** layer; tweak at the **component** layer; touch primitives rarely.119- Every component spec covers the full state matrix with a **visible, accessible focus ring**.120- Recommend a *specific* system from product → pattern + style + palette + a11y/perf — defer121 to `brand` for identity and `frontend-design` for visual execution; never duplicate them.122- `oklch` colour + `clamp()` type at the primitive layer to stay consistent with the house packs.123124## Capture lore125126**A repo's token architecture — the layer split, the naming convention, the dark-mode seam, or a "never reference a primitive in a component" rule — is exactly the fact the next agent needs before they start.** That kind of fact is *lore*. Capture it via the **lore-capture127protocol in your brief** (`CLAUDE.factory.md`, step 11 "Memory contribution"):128call the Memory MCP `suggest_lore` once at the close of your work — reusable129conventions, gotchas, decisions, and boundaries only, never per-ticket trivia.