Design principles
The rules behind every component. Constraints, not suggestions. Hover and interact to see them at work.
Animation
Controlled animation feels responsive. Slow or mismatched easing feels broken.
- Animate only
transformandopacity. These are GPU-composited; animating width, height, or position forces layout and drops frames. - Never animate from
scale(0), minimumscale(0.95). Elements should arrive from just off-stage, not materialize from nothing. ease-outfor entering,ease-in-outfor on-screen movement.- Cap all animations at
300ms. Anything longer feels like the interface is showing off. - Honor
prefers-reduced-motion. Every transition collapses to near-instant for users who request it. Handled globally, no per-component work.
Design Review
Weekly sync with the design team
API Migration
Move endpoints to v2 schema
Push Notifications
Set up alerts for critical events
Spacing
A consistent rhythm reduces cognitive load. The 4px base unit creates alignment you feel before you can name it.
Base unit and multipliers
Coupling: the gap signals the relationship
Tight (gap-1)
Title and description sit at 4px, so they read as one unit.
Loose (gap-4)
The same pair at 16px reads as two separate things.
Guidelines:
4pxbase unit via--spacing: 0.25rem. Tailwind multiplies:gap-2= 8px,p-4= 16px,h-8= 32px.gap-1(4px) for tightly related elements: icon-to-text, title-to-description.gap-4orgap-5between distinct groups: form fields, card sections, list items.
Layout and composition
Children own their spacing; containers own structure. Composition over configuration.
Children own their spacing
Notifications
Push and email alerts
Favorites
Saved items
One backdrop: blur supplies depth
Dialog, AlertDialog, Drawer, and Sheet share it. The blur de-emphasizes the page; the panel border defines its edge.
Guidelines:
- Children own their spacing; containers own structure (border, radius, shadow, bg). A container never reaches in to pad its children.
- Cards have a small set of interior layouts (header plus content, body, showcase, compact), applied to the children via shared classes like
cardHeaderClasscomposed withcn(), not ad-hoc padding. - One backdrop for every overlay (Dialog, AlertDialog, Drawer, Sheet):
bg-black/10 backdrop-blur-md. The blur supplies depth and de-emphasis; the panel border supplies the edge, no drop shadow. - Page content caps at a
1540pxcontainer. Mobile-first, with themdbreakpoint (768px) as the primary shift.
Radius
One radius scale, assigned by tier. The corner tells you what kind of thing it is.
rounded-xs
Checkbox, micro
rounded-sm
Menu items
rounded-md
Controls, cards
rounded-lg
Buttons, modals
rounded-xl
Drawers, sheets
rounded-2xl
Feature cards
rounded-full
Badges, pills
Guidelines:
- Radius is tier-assigned, not chosen per element. Form controls and cards use
rounded-md; buttons and modalsrounded-lg; drawers and sheetsrounded-xl; badges and pillsrounded-full. - The scale runs
rounded-xs(2px) torounded-2xl(16px). No arbitrary radii; extend the scale instead.
Elevation and layering
Shadows say how high a surface lifts; a named z-index scale says what stacks above what.
shadow-xs
Form controls
shadow-sm
Subtle elevation
shadow-md
Cards, overlays
shadow-lg
Modals, dialogs
Card surfaces
Shadows define elevation, borders define edges. Cards combine shadow-md for lift with border-neutral-200 / dark:border-neutral-800 for definition.
Named layers prevent z-index wars
--z-header50--z-dropdown60--z-overlay70--z-modal80--z-toast90--z-max999Guidelines:
- Multi-layer shadows for realistic depth. Same values work in both light and dark modes.
- Tier-assigned:
shadow-xsfor form controls,shadow-mdfor cards and overlays,shadow-lgfor modals and dialogs. Drawers and sheets pairshadow-mdwith a blurred backdrop. - Layering uses a named z-index scale, from
--z-raised(10) to--z-max(999), so stacking is intentional. No arbitraryz-9999; start a new stacking context withisolation: isolate.
Typography
Two families, one scale. Hierarchy comes from size, not weight.
Geist Sans
Measure and leading
Body copy is capped at a 60 to 75 character measure so the eye returns to a predictable left edge on every line. Leading opens up at reading sizes and tightens for headings, where the lines belong together.
Tabular figures align in columns
1,204.50
0,019.99
8,888.00
Guidelines:
font-sans(Geist Sans) for interface text,font-mono(Geist Mono) for code and technical values.- Hierarchy comes from size, not weight. Headings hold at
font-semibold; the kit ships no bold. - 12-step scale from
text-3xs(10px) totext-jumbo(45px). No arbitrary font sizes. - Body copy holds a 60 to 75 character measure with
leading-reading; headings tighten toleading-tightorleading-snug. tracking-wide(0.08em) for uppercase labels;tabular-numsfor columns of figures. Standard text uses default tracking.
Form controls
Consistent heights and focus styles. Interact with the states below.
Guidelines:
- Height:
h-8(32px) across Input, Select, and the Combobox trigger for a consistent vertical rhythm. Larger touch targets come from hit-area padding, not a taller control. - Mobile sizing: controls render
text-baseon mobile andmd:text-smon desktop. The 16px mobile size stops iOS Safari from zooming on focus. - Focus: text controls use
focus-visible:border-accent+focus-visible:ring-ring-accentring-1; Select and Switch usefocus-visible:ring-ring-focusring-[3px]. - Invalid:
aria-invalid:border-destructive+aria-invalid:ring-ring-destructive. Always pair the ring with a visible error message beside the field, never color alone. - Validation: validate on blur or submit, not on every keystroke, and colocate the message directly beneath the field.
- Disabled:
cursor-not-allowedwith reduced opacity. Input and Textarea useopacity-70, other controls useopacity-50. - Labels: every control is tied to its label with
htmlFor/id. Usesr-onlylabels only when a visible affordance already names the field.
Color
OKLCH for perceptual uniformity. Role-based tokens that retone from one variable and resolve per mode.
Primitive scales: 11 steps each
Neutral
Accent
Destructive
Warning
Success
Role, not hue: one variable retones the scale
--color-accent-h: 260.6--color-accent-h: 150Surfaces pair with a foreground
Page
Default page surface
--backgroundCard
Elevated panels
--cardMuted
Subdued backgrounds
--mutedAccent
Primary actions
--accentDestructive
Danger actions
--destructiveGuidelines:
- OKLCH color system: five primitive scales (neutral, accent, destructive, warning, success), each 11 steps (
50to950) with perceptually uniform lightness. - Role, not hue. Tokens name a role (
--accent,--destructive), never a color. Change--color-accent-hand every step of the scale retones together. - Semantic tokens resolve per mode:
bg-cardandtext-foregroundswap in:rootand.dark. Surfaces flip; the accent stays constant ataccent-500. - Intent colors (
--destructive,--warning,--success) only when the color carries information: a destructive action, a caution, a confirmation. - Alpha modifiers only on primitive scale tokens (
bg-accent-500/10); semantic tokens are used at full value, never with alpha.