Design Craft
Constraints + Consistency + Restraint = Quality. LLMs converge on the statistical median of every Tailwind tutorial and template — generic, safe, forgettable. This skill provides the decisions a strong designer would make, pre-made: executable kernels for type, color, spacing, and interaction, plus branch recipes for everything else.
Hard Guardrails
| Reflex |
Build instead |
| Purple/violet/indigo→pink/fuchsia gradients, gradient text, blurred gradient orbs |
One flat committed surface, real media, or a tonal treatment derived from your own scale |
| Decorative glass/glow/blur on static surfaces |
Named elevation (tint + shadow); glass only on overlays with a readable fallback |
| Centered hero + identical card grid; 3-4 equal big-number metric cards |
A content-driven composition: asymmetric anchor + supporting cells, or one prominent metric with context — reference/composition.md |
| Fake proof: "Trusted by 10,000+", logo marquees, "John Doe"/"Acme Corp", round metrics (99.99%), fake terminal/browser/mac-window chrome (traffic-light dots on mockups included) |
Real customers, real numbers, the real product surface without a fake window around it — or omit the section entirely |
| Colored left-border stripes on cards, callouts, event/schedule items, alerts |
Tinted surface OR a leading dot/chip/time column — one cue, never stripe + tint. Category color lives in a filled dot or label, not an edge bar (blockquote prose quotes exempt) |
| Unmodified component-library defaults (verbatim shadcn variant strings, default radii/tokens, Inter-by-reflex) |
Customize tokens, radius, and type to this product before composing pages |
Dead controls: href="#", empty onClick, toggles that don't persist |
Wire the real behavior or remove the control — a dead affordance is a broken promise |
| Attention-begging motion: pulse/ping on CTAs or pricing, identical fade-in-up on every section |
Motion communicates feedback, state, or spatial relationship, bounded to its role — reference/motion.md |
| Template copy: "Seamlessly", "Effortlessly", "Streamline", "Unlock", "It's not just X, it's Y", "Welcome to our platform", "Built with ❤️", "AI-powered" (non-AI product) |
The concrete claim: what the product does, for whom, in specific verbs |
The intent-and-repetition lens — for every context-sensitive treatment (warm neutrals, texture, illustration, large radii, uppercase micro-labels, sparklines, dark sections): the treatment must communicate state, hierarchy, focus, information, or brand meaning, and be bounded to a named role. One brand kicker is voice; an eyebrow above every section is scaffolding. A skeleton pulse is feedback; a pricing pulse is begging. A coherent warm neutral scale is a palette; a --cream token by reflex is the median. Token names are never the failure — unbounded repetition without purpose is.
Combination tells — individually forgivable, failing in combination (2+ together): eyebrow kickers on multiple sections, numbered section markers outside a real sequence, radius ≥24px outside a declared soft/playful direction, cards nested in cards, icon-heading-line cells repeated identically, different background color per section.
Project Context Scan (MANDATORY — before Design Decision Gate)
Determine brownfield (existing design system) or greenfield:
components.json → shadcn project, tokens in globals.css under :root
tailwind.config → theme.extend.colors custom tokens
globals.css / app/globals.css → CSS custom properties (--primary, --background, …)
package.json → UI libraries: shadcn, Radix, Panda CSS, vanilla-extract
ANY found → BROWNFIELD: catalog existing tokens and use them. Skip color derivation. Derive only genuinely missing roles, harmonized with existing hues.
None → GREENFIELD: run the full Design Decision Gate including color derivation.
Design Decision Gate (MANDATORY — before writing any code)
State these decisions explicitly in your reasoning:
- Structural archetype: app shell (sidebar + content) · marketing page (sections + CTA) · focused tool (centered workspace) · dashboard (data-dense grid) · editorial (long-form reading)
- Visual density: sparse · balanced · dense
- Aesthetic direction — a specific adjective pair matched to the product, not "modern" or "clean": stark technical, bold expressive, soft organic, restrained editorial, luxury refined, playful toy-like, retro-futuristic, industrial utilitarian.
- Initial mode from the physical scene — write one sentence: who uses this, where, under what light, in what mood. That sentence picks light or dark. Design the chosen mode first; derive the other via semantic token remapping, never as a separate design.
- Typeface — from the font menu below, matched to the aesthetic (availability rule in the type kernel).
- Color strategy — BROWNFIELD: list existing tokens. GREENFIELD: run the color kernel below.
- Composition — hero architecture from the menu below (marketing) or shell recipe from reference/composition.md (apps, dashboards, settings, detail pages).
- The unforgettable question: "If someone saw 10 similar pages today, what would make them remember THIS one?" Name one concrete visual or structural choice.
- The swap test + category-reflex check: If swapping the layout for a template and the font for Inter would go unnoticed, those choices defaulted — remake them. If the palette or aesthetic is guessable from the product category alone (or from category-plus-anti-reference: "fintech but not navy → terminal dark"), that's the training-data reflex one tier deep; rework until neither is obvious.
The archetype and density decisions drive everything else. The delight-impact curve: rare moments (onboarding complete, first success) earn more delight than daily actions. Polish is uniform — settings, empty states, and errors get the same care as the hero.
Reference loading triggers (MANDATORY):
- Palette, contrast, or dark-mode depth → READ reference/color.md
- Font loading, fallbacks, variable fonts, long-form text, CJK/multi-script → READ reference/typography.md
- Radius/elevation systems, density modes, block rhythm, safe areas, optical alignment → READ reference/spacing.md
- App shells, sidebars, gutters, metadata/date lockups, settings, features, pricing, proof, footers → READ reference/composition.md
- Async data, forms, validation, empty/error/loading, destructive actions, optimistic updates → READ reference/product-states.md
- Modals, drawers, transitions, gestures, any interactive motion → READ reference/motion.md
- Dashboards, tables, charts, KPIs, financial UI → READ reference/data-dense.md
- Icons, images, illustration, emoji → READ reference/visual-assets.md
Hero Menu (marketing pages)
| Architecture |
Layout |
Use when |
| Splitscreen |
55/45 or 60/40 text + media, asymmetric |
Product with strong visuals |
| Editorial |
Oversized display heading, minimal supporting text, no image |
Brand/statement pages |
| Full-bleed media |
Viewport-height image/video with text overlay |
Visual-first brands, portfolios |
| Asymmetric mosaic |
Unequal grid cells with mixed media + text |
Creative agencies, portfolios |
| Anchored metric |
One hero stat with context paragraph |
SaaS, data products |
Hero rules: spacing min py-24 md:py-32; headline sizes from the type kernel with a clamp() ceiling of 6rem — a larger ceiling is allowed only when visual scale IS the feature and the live render proves it survives every breakpoint. Subtext max 2 sentences, max-w-[50ch]. One CTA; secondary action is a text link. Real imagery with alt text, or bold typography as the visual — never colored boxes or icon grids. Heading copy must survive every breakpoint.
Type Kernel
Five roles with default sizes — adjust deliberately per aesthetic, never by reflex: Display clamp(2.5rem, 1.5rem + 4vw, 6rem) (hero statements) · Heading 1.5–2.25rem (section/page titles) · Body 1rem (reading text) · Caption 0.875rem (supporting meta) · Micro 0.75rem (labels, badges). Fewer sizes with more contrast beat many close sizes — app UI rarely needs more than these five plus one intermediate heading step.
| Rule |
Value |
| Tracking |
≥48px: -0.02em · 30–47px: -0.01em · <30px: 0 · uppercase micro-labels only: +0.05em. After rendering, adjust at most ±0.005em for the chosen face. Hard floor -0.03em; tracking-tighter fails. |
| Line height |
Display 1.0–1.1 · Heading 1.2–1.3 · Body 1.5–1.6 · short UI labels 1.0–1.2. Unitless. |
| Case |
Sentence case for headings, labels, tabs, buttons. ALL CAPS only for micro-meta ≤3 words. |
| Numerals |
tabular-nums + lining figures on all data — prices, counts, dates, IDs, table columns. Right-align numeric columns. |
| Wrapping |
Headings text-balance, body text-pretty, measure max-w-[65ch]. |
| Weights |
Max 3 per view; minimum range 400/500/600 — two-weight interfaces lack nuance. |
| Mobile inputs |
≥16px or iOS zooms. Never user-scalable=no. |
| Availability |
Verify the font's source/license/package before selecting. System font stacks are valid for brownfield and performance-first UI. |
Font menu by aesthetic (open menu — any verified font that matches the direction qualifies):
| Aesthetic |
Examples |
| Editorial |
Instrument Sans, Source Sans 3, Libre Franklin |
| Technical / stark |
Geist, IBM Plex Sans, Commit Mono (accents) |
| Friendly / soft |
Plus Jakarta Sans, Nunito Sans, Outfit |
| Bold / expressive |
Sora, Space Grotesk, Clash Display (display only) |
| Neutral / professional |
General Sans, Satoshi, Switzer |
Reaching for Inter/Roboto/DM Sans "just to pick something" is defaulting — return to the aesthetic direction. One family unless genuine display/body contrast (serif+sans, geometric+humanist). Monospace as small accent (font-mono text-xs on IDs, timestamps) creates productive contrast in technical products; monospace display headings do not.
Loading strategy, fallback metrics, variable fonts, CJK: reference/typography.md.
Color Kernel
Strategy axis — state before picking colors: Restrained (tinted neutrals + one accent ≤10%, default for tools/dashboards, chroma ×0.6) · Committed (one saturated color carries 30–60%, marketing, ×1.0) · Full palette (3-4 named roles, campaigns/data viz, ×1.2) · Drenched (the surface IS the color, heroes, ×1.4). The multiplier scales every stop's chroma; re-clamp against the gamut caps after scaling. "Restrained by reflex" is the same failure as "Inter by reflex."
GREENFIELD derivation (executable, in order):
- Hue from the product name:
H = (first_letter_ordinal × 137 + second_letter_ordinal × 47) mod 360 (a=1…z=26; skip non-letters, and if fewer than two letters exist, reuse the first). Never replace with a category stereotype. If the derived hue cannot meet contrast/gamut after capping, shift ±30° and re-derive. A solid committed surface at the derived hue is always legal — the gradient guardrail bans gradient treatments, not hues.
- 12-stop lightness spine at the derived hue:
L = 0.99, 0.96, 0.90, 0.82, 0.71, 0.64, 0.55, 0.49, 0.40, 0.32, 0.27, 0.24 (stops 50…950). Committed-strategy chroma per stop: C = 0.02, 0.04, 0.06, 0.09, 0.11, 0.13, 0.13, 0.12, 0.10, 0.08, 0.06, 0.05 — scale by the strategy multiplier, then cap C at 0.09 for hues 170–210° and ~`0.18` elsewhere (sRGB safety).
- Companion neutral scale at
H+180° (≥120° separation), C ≤ 0.02 — the tinted neutrals that replace dead white/gray. Never #fff/#000 as surfaces; use stop-50/stop-100.
- Map to semantic tokens:
--background --foreground --card --primary --secondary --muted --accent --destructive --border --ring. Components use ONLY semantic tokens — never bg-blue-500, never inline hex/rgb/oklch in markup. Opacity modifiers on tokens are allowed (bg-primary/10).
- Contrast floors (WCAG 2.x conformance): 4.5:1 normal text, 3:1 large text, 3:1 for required non-text UI (borders of inputs, icons carrying meaning, focus rings) — measured against the actual rendered background, placeholders included. Fix failures by moving L, not C. Every status conveys through a non-color cue too (icon, text, weight).
- Dark mode is a remap: invert the semantic lightness mapping onto tonal surfaces (lighter = closer), reduce chroma ~10–20%, prefer surface tint over shadow, weight 350 only when the loaded font provides it. Swap the semantic layer, not components.
Universal status colors — success=green, error/destructive=red, warning=amber, info=blue. Users already know these; never invert, never use brand accent for status.
60-30-10: 60% neutrals, 30% secondary, 10% accent; one accent at 100/60/20/10% opacity before adding a second. All section backgrounds come from the same neutral scale, and a landing page is one continuous canvas: at most one emphasis panel + the footer change surface, no full-width rules between sections — variety comes from density, typography, and rhythm. Token names reveal the product (--meridian-600, not --gray-700).
Gamut mapping, P3, warm-neutral branch, APCA (supplementary only), dark-mode depth: reference/color.md.
Spacing Kernel
4px grid — all spacing in multiples of 4.
tight: 4px — within atomic elements
item: 8px — between items in a group
group: 16px — between related groups
section: 24–32px — between major sections
page: 32–64px — page-level margins
| Rule |
Value |
| Inset vs stack |
Inset = padding inside a component; stack = gap between siblings. Set them independently — equal inset+stack everywhere is how "same padding everywhere" happens. |
| Rhythm |
Tight within groups, larger between groups, varied section density. |
| Grouping cue |
Pick ONE per region: spacing, border, or elevation — never all three. |
| Surfaces |
Max 3 visible surface levels (page → section → card). |
| Radius |
One base radius, derive the rest; concentric nesting: outer = inner + padding. Scale values and aesthetic exceptions: reference/spacing.md. |
| Targets |
Mobile web ≥44×44 CSS px (WCAG AA floor is 24px; Android-native prefers 48dp). Extend with pseudo-elements when the visible element is smaller. Adjacent hit areas never overlap. |
| Tokens |
Framework scale only — never p-[13px], gap-[7px]. Acceptable arbitrary: max-w-[65ch], min-h-[*rem], grid templates. |
| Height |
Comes from content + the ladder, never from min-height + vertical centering (sole exception: a full-viewport hero where scale is the declared feature). Empty bands between blocks within one surface: ≤64px app/data, ≤128px marketing. At section boundaries stacked paddings may total ~192px on marketing pages; >256px is dead air anywhere. Larger = unowned gap: pull the next block up or give the band content. |
Density modes, block text rhythm, elevation ladder, shadows, safe areas, optical alignment: reference/spacing.md. Shell geometry, gutters, lockups: reference/composition.md.
Layout Mechanics
| Rule |
Detail |
| Structure |
Flexbox for 1D, Grid for 2D. Never absolute positioning for structural layout. |
| Viewport |
h-dvh, never h-screen (iOS Safari). |
| Overflow |
min-w-0 on flex/grid children with text. overflow: hidden is banned as layout repair — fix the content or the container. |
| Z-index |
Fixed semantic scale (dropdown → sticky → backdrop → modal → toast → tooltip), never z-[999]. |
| Overlays |
Dropdowns/tooltips inside overflow: hidden/auto ancestors get clipped — use the Popover API, position: fixed, or a portal. |
| Sizing contract |
Declare per element: Hug (wraps content), Fill (flex: 1), or Fixed (sidebars, avatars). |
| Grids |
No empty cells: remainder 1 → last item spans the row; remainder 2 → both span half. auto-fill + minmax(280px, 1fr) for variable counts. |
| Semantic HTML |
<button> for actions, <a> for navigation — never <div onClick>. Icon-only controls get aria-label. |
| Responsive |
2-tier default (mobile + desktop). @container queries when the same component serves different-width regions. Never hide core functionality on mobile. Test extremes: 100+ char strings, emoji, RTL, empty, 1000+ items. |
| Asymmetry |
grid-cols-[2fr_1fr] beats equal columns; vary section density; whitespace is composition — consistent and framed, not accidental. |
Structural completeness: an app shell has navigation context + full-height content (a form in a void is missing structure); a dashboard fills its grid; a detail page pairs primary content with supporting context. Complete recipes: reference/composition.md.
Sidebar: same hue family as content, 1–2 lightness steps apart — no chroma cliff between shell and content. Inactive items recede (reduced opacity), active state is tonal (no side-stripe). Content always wins the hierarchy contest. Full anatomy: reference/composition.md.
Interaction & Completeness Kernel
Every interactive element has all applicable states: default, hover (gated @media (hover: hover)), focus-visible (visible ring — never removed), active (pressed feedback), disabled. Every async action runs the lifecycle idle → pending → success/error: pending disables re-activation and shows status (aria-busy), success/error is visible where the action happened.
| Rule |
Detail |
| Async data surfaces |
Design all four: loading, populated, empty, error — before shipping any of them. Recipes: reference/product-states.md. |
| Empty states |
Fact + context, plus a next action when one truly exists. |
| Errors |
Inline, adjacent to the cause: what happened, why, how to fix. No jargon, no humor. |
| Destructive actions |
Reversible → immediate + undo window. Irreversible/high-stakes → explicit confirmation naming what and how much. |
| Optimistic UI |
Safe reversible actions only; visible rollback on failure — never payments or deletions. |
| Loading |
Structural skeletons matching content shape, not generic spinners. |
| Buttons |
Label never wraps (white-space: nowrap) — shorten the label or widen the button. Verb+object: "Save changes", not "OK". |
| Primitives |
Accessible primitives (shadcn/Radix/Base UI) customized to the project; never rebuild keyboard/focus behavior; never mix systems. |
| Formatting |
Intl.NumberFormat/Intl.DateTimeFormat, never string concatenation. Never block paste. |
UX Writing
Specific verb + object on buttons. One term per concept (Delete/Remove → pick one). Active voice. Cut every sentence in half, then again. Budget +30–40% width for i18n. Errors: never humor, never raw jargon (500, ECONNREFUSED). Never restate the heading in an intro.
Symptom → Correction
| Symptom |
Likely cause |
Fix |
| Decent but generic |
Equal-weight cards, uniform padding |
Remove a widget, merge a region, flatten one surface |
| Everything competes |
Multiple primary-weight elements |
ONE dominant element, mute the rest |
| Cramped |
Uniform tight spacing |
Increase section gaps, keep group spacing tight |
| Empty despite content |
Oversized gaps, thin type |
Tighten item spacing, add weight to key elements |
| Floaty content islands |
min-height + centering, stacked margins, uniform huge section gaps |
Height from content; snap every gap to the ladder; kill voids over the Height rule's band limits |
| Ragged two-column section |
Heading, controls, and aside start at different y |
One section origin: heading row baseline-aligned, both columns start one group unit below it |
| Clips on real content |
Fixed heights |
min-h + overflow-y-auto on scroll regions |
| Dark mode washed out |
Same chroma/weight as light |
Reduce chroma ~10–20%, reduce weight where the font allows |
| Numbers jump on update |
Missing tabular-nums |
font-variant-numeric: tabular-nums |
| Sidebar fights content |
Too bright, equal weight |
Dim shell 1–2 steps, inactive items to reduced opacity |
Conflict Priority
Sacrifice in this order (last = drop first): 1. functional integrity → 2. readability → 3. spatial rhythm → 4. motion polish → 5. decorative novelty.
Self-Check (MANDATORY — before finishing any UI work)
Re-read every line you wrote. Verify each item; fix failures before responding.
- Guardrails — zero hard-guardrail patterns; combination tells don't stack.
- Tokens only — every color is a semantic token; zero inline hex/rgb/oklch in markup; zero arbitrary spacing/type values.
- Type kernel — tracking table honored,
tracking-tighter absent, one h1, heading levels don't skip.
- States complete — every interactive element has its five states; every async surface has loading/populated/empty/error; every async action blocks duplicate activation.
- Layout mechanics —
h-dvh, min-w-0 where text can overflow, no empty grid cells, no wrapped button labels.
- The swap test — could the font become Inter and the layout a centered template without anyone noticing? If yes, remake those decisions.
- References honored — name which reference files you read this session. If a mandatory trigger fired and its file went unread, read it now and re-verify the affected code.
When the work claims done, run the design-qa gates (scanner + live render).
1---2name: design-craft3description: Anti-slop UI craft rules that break out of the AI-generated median. Use when building or restyling any UI — pages, components, dashboards, forms, landing pages — when existing UI looks generic or machine-made, or when another skill needs rules for typography, color, spacing, layout, composition, motion, product states, or interaction.4---56# Design Craft78Constraints + Consistency + Restraint = Quality. LLMs converge on the statistical median of every Tailwind tutorial and template — generic, safe, forgettable. This skill provides the decisions a strong designer would make, pre-made: executable kernels for type, color, spacing, and interaction, plus branch recipes for everything else.910---1112## Hard Guardrails1314| Reflex | Build instead |15|---|---|16| Purple/violet/indigo→pink/fuchsia gradients, gradient text, blurred gradient orbs | One flat committed surface, real media, or a tonal treatment derived from your own scale |17| Decorative glass/glow/blur on static surfaces | Named elevation (tint + shadow); glass only on overlays with a readable fallback |18| Centered hero + identical card grid; 3-4 equal big-number metric cards | A content-driven composition: asymmetric anchor + supporting cells, or one prominent metric with context — [reference/composition.md](reference/composition.md) |19| Fake proof: "Trusted by 10,000+", logo marquees, "John Doe"/"Acme Corp", round metrics (99.99%), fake terminal/browser/mac-window chrome (traffic-light dots on mockups included) | Real customers, real numbers, the real product surface without a fake window around it — or omit the section entirely |20| Colored left-border stripes on cards, callouts, event/schedule items, alerts | Tinted surface OR a leading dot/chip/time column — one cue, never stripe + tint. Category color lives in a filled dot or label, not an edge bar (`blockquote` prose quotes exempt) |21| Unmodified component-library defaults (verbatim shadcn variant strings, default radii/tokens, Inter-by-reflex) | Customize tokens, radius, and type to this product before composing pages |22| Dead controls: `href="#"`, empty onClick, toggles that don't persist | Wire the real behavior or remove the control — a dead affordance is a broken promise |23| Attention-begging motion: pulse/ping on CTAs or pricing, identical fade-in-up on every section | Motion communicates feedback, state, or spatial relationship, bounded to its role — [reference/motion.md](reference/motion.md) |24| Template copy: "Seamlessly", "Effortlessly", "Streamline", "Unlock", "It's not just X, it's Y", "Welcome to our platform", "Built with ❤️", "AI-powered" (non-AI product) | The concrete claim: what the product does, for whom, in specific verbs |2526**The intent-and-repetition lens** — for every context-sensitive treatment (warm neutrals, texture, illustration, large radii, uppercase micro-labels, sparklines, dark sections): the treatment must communicate state, hierarchy, focus, information, or brand meaning, and be bounded to a named role. One brand kicker is voice; an eyebrow above every section is scaffolding. A skeleton pulse is feedback; a pricing pulse is begging. A coherent warm neutral scale is a palette; a `--cream` token by reflex is the median. Token names are never the failure — unbounded repetition without purpose is.2728**Combination tells** — individually forgivable, failing in combination (2+ together): eyebrow kickers on multiple sections, numbered section markers outside a real sequence, radius ≥24px outside a declared soft/playful direction, cards nested in cards, icon-heading-line cells repeated identically, different background color per section.2930---3132## Project Context Scan (MANDATORY — before Design Decision Gate)3334Determine **brownfield** (existing design system) or **greenfield**:35361. `components.json` → shadcn project, tokens in `globals.css` under `:root`372. `tailwind.config` → `theme.extend.colors` custom tokens383. `globals.css` / `app/globals.css` → CSS custom properties (`--primary`, `--background`, …)394. `package.json` → UI libraries: shadcn, Radix, Panda CSS, vanilla-extract4041**ANY found → BROWNFIELD:** catalog existing tokens and use them. Skip color derivation. Derive only genuinely missing roles, harmonized with existing hues.42**None → GREENFIELD:** run the full Design Decision Gate including color derivation.4344---4546## Design Decision Gate (MANDATORY — before writing any code)4748State these decisions explicitly in your reasoning:49501. **Structural archetype:** app shell (sidebar + content) · marketing page (sections + CTA) · focused tool (centered workspace) · dashboard (data-dense grid) · editorial (long-form reading)512. **Visual density:** sparse · balanced · dense523. **Aesthetic direction** — a specific adjective pair matched to the product, not "modern" or "clean": stark technical, bold expressive, soft organic, restrained editorial, luxury refined, playful toy-like, retro-futuristic, industrial utilitarian.534. **Initial mode from the physical scene** — write one sentence: who uses this, where, under what light, in what mood. That sentence picks light or dark. Design the chosen mode first; derive the other via semantic token remapping, never as a separate design.545. **Typeface** — from the font menu below, matched to the aesthetic (availability rule in the type kernel).556. **Color strategy** — BROWNFIELD: list existing tokens. GREENFIELD: run the color kernel below.567. **Composition** — hero architecture from the menu below (marketing) or shell recipe from [reference/composition.md](reference/composition.md) (apps, dashboards, settings, detail pages).578. **The unforgettable question:** "If someone saw 10 similar pages today, what would make them remember THIS one?" Name one concrete visual or structural choice.589. **The swap test + category-reflex check:** If swapping the layout for a template and the font for Inter would go unnoticed, those choices defaulted — remake them. If the palette or aesthetic is guessable from the product category alone (or from category-plus-anti-reference: "fintech but not navy → terminal dark"), that's the training-data reflex one tier deep; rework until neither is obvious.5960The archetype and density decisions drive everything else. **The delight-impact curve:** rare moments (onboarding complete, first success) earn more delight than daily actions. Polish is uniform — settings, empty states, and errors get the same care as the hero.6162**Reference loading triggers (MANDATORY):**63- Palette, contrast, or dark-mode depth → **READ [reference/color.md](reference/color.md)**64- Font loading, fallbacks, variable fonts, long-form text, CJK/multi-script → **READ [reference/typography.md](reference/typography.md)**65- Radius/elevation systems, density modes, block rhythm, safe areas, optical alignment → **READ [reference/spacing.md](reference/spacing.md)**66- App shells, sidebars, gutters, metadata/date lockups, settings, features, pricing, proof, footers → **READ [reference/composition.md](reference/composition.md)**67- Async data, forms, validation, empty/error/loading, destructive actions, optimistic updates → **READ [reference/product-states.md](reference/product-states.md)**68- Modals, drawers, transitions, gestures, any interactive motion → **READ [reference/motion.md](reference/motion.md)**69- Dashboards, tables, charts, KPIs, financial UI → **READ [reference/data-dense.md](reference/data-dense.md)**70- Icons, images, illustration, emoji → **READ [reference/visual-assets.md](reference/visual-assets.md)**7172---7374## Hero Menu (marketing pages)7576| Architecture | Layout | Use when |77|---|---|---|78| **Splitscreen** | 55/45 or 60/40 text + media, asymmetric | Product with strong visuals |79| **Editorial** | Oversized display heading, minimal supporting text, no image | Brand/statement pages |80| **Full-bleed media** | Viewport-height image/video with text overlay | Visual-first brands, portfolios |81| **Asymmetric mosaic** | Unequal grid cells with mixed media + text | Creative agencies, portfolios |82| **Anchored metric** | One hero stat with context paragraph | SaaS, data products |8384Hero rules: spacing min `py-24 md:py-32`; headline sizes from the type kernel with a `clamp()` ceiling of 6rem — a larger ceiling is allowed only when visual scale IS the feature and the live render proves it survives every breakpoint. Subtext max 2 sentences, `max-w-[50ch]`. One CTA; secondary action is a text link. Real imagery with alt text, or bold typography as the visual — never colored boxes or icon grids. Heading copy must survive every breakpoint.8586---8788## Type Kernel8990Five roles with default sizes — adjust deliberately per aesthetic, never by reflex: **Display** `clamp(2.5rem, 1.5rem + 4vw, 6rem)` (hero statements) · **Heading** `1.5–2.25rem` (section/page titles) · **Body** `1rem` (reading text) · **Caption** `0.875rem` (supporting meta) · **Micro** `0.75rem` (labels, badges). Fewer sizes with more contrast beat many close sizes — app UI rarely needs more than these five plus one intermediate heading step.9192| Rule | Value |93|---|---|94| Tracking | ≥48px: `-0.02em` · 30–47px: `-0.01em` · <30px: `0` · uppercase micro-labels only: `+0.05em`. After rendering, adjust at most `±0.005em` for the chosen face. Hard floor `-0.03em`; `tracking-tighter` fails. |95| Line height | Display `1.0–1.1` · Heading `1.2–1.3` · Body `1.5–1.6` · short UI labels `1.0–1.2`. Unitless. |96| Case | Sentence case for headings, labels, tabs, buttons. ALL CAPS only for micro-meta ≤3 words. |97| Numerals | `tabular-nums` + lining figures on all data — prices, counts, dates, IDs, table columns. Right-align numeric columns. |98| Wrapping | Headings `text-balance`, body `text-pretty`, measure `max-w-[65ch]`. |99| Weights | Max 3 per view; minimum range 400/500/600 — two-weight interfaces lack nuance. |100| Mobile inputs | ≥16px or iOS zooms. Never `user-scalable=no`. |101| Availability | Verify the font's source/license/package before selecting. System font stacks are valid for brownfield and performance-first UI. |102103**Font menu by aesthetic** (open menu — any verified font that matches the direction qualifies):104105| Aesthetic | Examples |106|---|---|107| Editorial | Instrument Sans, Source Sans 3, Libre Franklin |108| Technical / stark | Geist, IBM Plex Sans, Commit Mono (accents) |109| Friendly / soft | Plus Jakarta Sans, Nunito Sans, Outfit |110| Bold / expressive | Sora, Space Grotesk, Clash Display (display only) |111| Neutral / professional | General Sans, Satoshi, Switzer |112113Reaching for Inter/Roboto/DM Sans "just to pick something" is defaulting — return to the aesthetic direction. One family unless genuine display/body contrast (serif+sans, geometric+humanist). Monospace as small accent (`font-mono text-xs` on IDs, timestamps) creates productive contrast in technical products; monospace display headings do not.114115Loading strategy, fallback metrics, variable fonts, CJK: [reference/typography.md](reference/typography.md).116117---118119## Color Kernel120121**Strategy axis** — state before picking colors: **Restrained** (tinted neutrals + one accent ≤10%, default for tools/dashboards, chroma ×0.6) · **Committed** (one saturated color carries 30–60%, marketing, ×1.0) · **Full palette** (3-4 named roles, campaigns/data viz, ×1.2) · **Drenched** (the surface IS the color, heroes, ×1.4). The multiplier scales every stop's chroma; re-clamp against the gamut caps after scaling. "Restrained by reflex" is the same failure as "Inter by reflex."122123**GREENFIELD derivation (executable, in order):**1241251. **Hue from the product name:** `H = (first_letter_ordinal × 137 + second_letter_ordinal × 47) mod 360` (a=1…z=26; skip non-letters, and if fewer than two letters exist, reuse the first). Never replace with a category stereotype. If the derived hue cannot meet contrast/gamut after capping, shift ±30° and re-derive. A solid committed surface at the derived hue is always legal — the gradient guardrail bans gradient treatments, not hues.1262. **12-stop lightness spine** at the derived hue: `L = 0.99, 0.96, 0.90, 0.82, 0.71, 0.64, 0.55, 0.49, 0.40, 0.32, 0.27, 0.24` (stops 50…950). Committed-strategy chroma per stop: `C = 0.02, 0.04, 0.06, 0.09, 0.11, 0.13, 0.13, 0.12, 0.10, 0.08, 0.06, 0.05` — scale by the strategy multiplier, then cap C at `0.09` for hues 170–210° and ~`0.18` elsewhere (sRGB safety).1273. **Companion neutral scale** at `H+180°` (≥120° separation), C ≤ 0.02 — the tinted neutrals that replace dead white/gray. Never `#fff`/`#000` as surfaces; use stop-50/stop-100.1284. **Map to semantic tokens:** `--background --foreground --card --primary --secondary --muted --accent --destructive --border --ring`. Components use ONLY semantic tokens — never `bg-blue-500`, never inline hex/rgb/oklch in markup. Opacity modifiers on tokens are allowed (`bg-primary/10`).1295. **Contrast floors (WCAG 2.x conformance):** 4.5:1 normal text, 3:1 large text, 3:1 for required non-text UI (borders of inputs, icons carrying meaning, focus rings) — measured against the actual rendered background, placeholders included. Fix failures by moving L, not C. Every status conveys through a non-color cue too (icon, text, weight).1306. **Dark mode is a remap:** invert the semantic lightness mapping onto tonal surfaces (lighter = closer), reduce chroma ~10–20%, prefer surface tint over shadow, weight 350 only when the loaded font provides it. Swap the semantic layer, not components.131132**Universal status colors** — success=green, error/destructive=red, warning=amber, info=blue. Users already know these; never invert, never use brand accent for status.13313460-30-10: 60% neutrals, 30% secondary, 10% accent; one accent at 100/60/20/10% opacity before adding a second. All section backgrounds come from the same neutral scale, and a landing page is one continuous canvas: at most one emphasis panel + the footer change surface, no full-width rules between sections — variety comes from density, typography, and rhythm. Token names reveal the product (`--meridian-600`, not `--gray-700`).135136Gamut mapping, P3, warm-neutral branch, APCA (supplementary only), dark-mode depth: [reference/color.md](reference/color.md).137138---139140## Spacing Kernel1411424px grid — all spacing in multiples of 4.143144```145tight: 4px — within atomic elements146item: 8px — between items in a group147group: 16px — between related groups148section: 24–32px — between major sections149page: 32–64px — page-level margins150```151152| Rule | Value |153|---|---|154| Inset vs stack | Inset = padding inside a component; stack = gap between siblings. Set them independently — equal inset+stack everywhere is how "same padding everywhere" happens. |155| Rhythm | Tight within groups, larger between groups, varied section density. |156| Grouping cue | Pick ONE per region: spacing, border, or elevation — never all three. |157| Surfaces | Max 3 visible surface levels (page → section → card). |158| Radius | One base radius, derive the rest; concentric nesting: outer = inner + padding. Scale values and aesthetic exceptions: [reference/spacing.md](reference/spacing.md). |159| Targets | Mobile web ≥44×44 CSS px (WCAG AA floor is 24px; Android-native prefers 48dp). Extend with pseudo-elements when the visible element is smaller. Adjacent hit areas never overlap. |160| Tokens | Framework scale only — never `p-[13px]`, `gap-[7px]`. Acceptable arbitrary: `max-w-[65ch]`, `min-h-[*rem]`, grid templates. |161| Height | Comes from content + the ladder, never from `min-height` + vertical centering (sole exception: a full-viewport hero where scale is the declared feature). Empty bands between blocks **within one surface**: ≤64px app/data, ≤128px marketing. At section boundaries stacked paddings may total ~192px on marketing pages; >256px is dead air anywhere. Larger = unowned gap: pull the next block up or give the band content. |162163Density modes, block text rhythm, elevation ladder, shadows, safe areas, optical alignment: [reference/spacing.md](reference/spacing.md). Shell geometry, gutters, lockups: [reference/composition.md](reference/composition.md).164165---166167## Layout Mechanics168169| Rule | Detail |170|---|---|171| Structure | Flexbox for 1D, Grid for 2D. Never absolute positioning for structural layout. |172| Viewport | `h-dvh`, never `h-screen` (iOS Safari). |173| Overflow | `min-w-0` on flex/grid children with text. `overflow: hidden` is banned as layout repair — fix the content or the container. |174| Z-index | Fixed semantic scale (dropdown → sticky → backdrop → modal → toast → tooltip), never `z-[999]`. |175| Overlays | Dropdowns/tooltips inside `overflow: hidden/auto` ancestors get clipped — use the Popover API, `position: fixed`, or a portal. |176| Sizing contract | Declare per element: **Hug** (wraps content), **Fill** (`flex: 1`), or **Fixed** (sidebars, avatars). |177| Grids | No empty cells: remainder 1 → last item spans the row; remainder 2 → both span half. `auto-fill` + `minmax(280px, 1fr)` for variable counts. |178| Semantic HTML | `<button>` for actions, `<a>` for navigation — never `<div onClick>`. Icon-only controls get `aria-label`. |179| Responsive | 2-tier default (mobile + desktop). `@container` queries when the same component serves different-width regions. Never hide core functionality on mobile. Test extremes: 100+ char strings, emoji, RTL, empty, 1000+ items. |180| Asymmetry | `grid-cols-[2fr_1fr]` beats equal columns; vary section density; whitespace is composition — consistent and framed, not accidental. |181182**Structural completeness:** an app shell has navigation context + full-height content (a form in a void is missing structure); a dashboard fills its grid; a detail page pairs primary content with supporting context. Complete recipes: [reference/composition.md](reference/composition.md).183184**Sidebar:** same hue family as content, 1–2 lightness steps apart — no chroma cliff between shell and content. Inactive items recede (reduced opacity), active state is tonal (no side-stripe). Content always wins the hierarchy contest. Full anatomy: [reference/composition.md](reference/composition.md).185186---187188## Interaction & Completeness Kernel189190Every interactive element has all applicable states: **default, hover** (gated `@media (hover: hover)`), **focus-visible** (visible ring — never removed), **active** (pressed feedback), **disabled**. Every async action runs the lifecycle **idle → pending → success/error**: pending disables re-activation and shows status (`aria-busy`), success/error is visible where the action happened.191192| Rule | Detail |193|---|---|194| Async data surfaces | Design all four: loading, populated, empty, error — before shipping any of them. Recipes: [reference/product-states.md](reference/product-states.md). |195| Empty states | Fact + context, plus a next action when one truly exists. |196| Errors | Inline, adjacent to the cause: what happened, why, how to fix. No jargon, no humor. |197| Destructive actions | Reversible → immediate + undo window. Irreversible/high-stakes → explicit confirmation naming what and how much. |198| Optimistic UI | Safe reversible actions only; visible rollback on failure — never payments or deletions. |199| Loading | Structural skeletons matching content shape, not generic spinners. |200| Buttons | Label never wraps (`white-space: nowrap`) — shorten the label or widen the button. Verb+object: "Save changes", not "OK". |201| Primitives | Accessible primitives (shadcn/Radix/Base UI) customized to the project; never rebuild keyboard/focus behavior; never mix systems. |202| Formatting | `Intl.NumberFormat`/`Intl.DateTimeFormat`, never string concatenation. Never block paste. |203204---205206## UX Writing207208Specific verb + object on buttons. One term per concept (Delete/Remove → pick one). Active voice. Cut every sentence in half, then again. Budget +30–40% width for i18n. Errors: never humor, never raw jargon (500, ECONNREFUSED). Never restate the heading in an intro.209210---211212## Symptom → Correction213214| Symptom | Likely cause | Fix |215|---|---|---|216| Decent but generic | Equal-weight cards, uniform padding | Remove a widget, merge a region, flatten one surface |217| Everything competes | Multiple primary-weight elements | ONE dominant element, mute the rest |218| Cramped | Uniform tight spacing | Increase section gaps, keep group spacing tight |219| Empty despite content | Oversized gaps, thin type | Tighten item spacing, add weight to key elements |220| Floaty content islands | `min-height` + centering, stacked margins, uniform huge section gaps | Height from content; snap every gap to the ladder; kill voids over the Height rule's band limits |221| Ragged two-column section | Heading, controls, and aside start at different y | One section origin: heading row baseline-aligned, both columns start one group unit below it |222| Clips on real content | Fixed heights | `min-h` + `overflow-y-auto` on scroll regions |223| Dark mode washed out | Same chroma/weight as light | Reduce chroma ~10–20%, reduce weight where the font allows |224| Numbers jump on update | Missing tabular-nums | `font-variant-numeric: tabular-nums` |225| Sidebar fights content | Too bright, equal weight | Dim shell 1–2 steps, inactive items to reduced opacity |226227---228229## Conflict Priority230231Sacrifice in this order (last = drop first): 1. functional integrity → 2. readability → 3. spatial rhythm → 4. motion polish → 5. decorative novelty.232233---234235## Self-Check (MANDATORY — before finishing any UI work)236237Re-read every line you wrote. Verify each item; fix failures before responding.2382391. **Guardrails** — zero hard-guardrail patterns; combination tells don't stack.2402. **Tokens only** — every color is a semantic token; zero inline hex/rgb/oklch in markup; zero arbitrary spacing/type values.2413. **Type kernel** — tracking table honored, `tracking-tighter` absent, one h1, heading levels don't skip.2424. **States complete** — every interactive element has its five states; every async surface has loading/populated/empty/error; every async action blocks duplicate activation.2435. **Layout mechanics** — `h-dvh`, `min-w-0` where text can overflow, no empty grid cells, no wrapped button labels.2446. **The swap test** — could the font become Inter and the layout a centered template without anyone noticing? If yes, remake those decisions.2457. **References honored** — name which reference files you read this session. If a mandatory trigger fired and its file went unread, read it now and re-verify the affected code.246247When the work claims done, run the `design-qa` gates (scanner + live render).