UI.MD

Search documentation

Search for components, guides, and tokens

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 transform and opacity. These are GPU-composited; animating width, height, or position forces layout and drops frames.
  • Never animate from scale(0), minimum scale(0.95). Elements should arrive from just off-stage, not materialize from nothing.
  • ease-out for entering, ease-in-out for 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

In Progress

API Migration

Move endpoints to v2 schema

Planned

Push Notifications

Set up alerts for critical events

Done

Spacing

A consistent rhythm reduces cognitive load. The 4px base unit creates alignment you feel before you can name it.

Base unit and multipliers

gap-1
4px
gap-2
8px
p-3
12px
gap-4
16px
p-5
20px
h-8
32px

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:

  • 4px base 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-4 or gap-5 between 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

Settings

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 cardHeaderClass composed with cn(), 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 1540px container. Mobile-first, with the md breakpoint (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 modals rounded-lg; drawers and sheets rounded-xl; badges and pills rounded-full.
  • The scale runs rounded-xs (2px) to rounded-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

shadow-md with border-neutral-200

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-max999

Guidelines:

  • Multi-layer shadows for realistic depth. Same values work in both light and dark modes.
  • Tier-assigned: shadow-xs for form controls, shadow-md for cards and overlays, shadow-lg for modals and dialogs. Drawers and sheets pair shadow-md with a blurred backdrop.
  • Layering uses a named z-index scale, from --z-raised (10) to --z-max (999), so stacking is intentional. No arbitrary z-9999; start a new stacking context with isolation: isolate.

Typography

Two families, one scale. Hierarchy comes from size, not weight.

Caption text
Body text
Lead text
Heading 4
Heading 3
Heading 2
Heading 1

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) to text-jumbo (45px). No arbitrary font sizes.
  • Body copy holds a 60 to 75 character measure with leading-reading; headings tighten to leading-tight or leading-snug.
  • tracking-wide (0.08em) for uppercase labels; tabular-nums for 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-base on mobile and md:text-sm on desktop. The 16px mobile size stops iOS Safari from zooming on focus.
  • Focus: text controls use focus-visible:border-accent + focus-visible:ring-ring-accent ring-1; Select and Switch use focus-visible:ring-ring-focus ring-[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-allowed with reduced opacity. Input and Textarea use opacity-70, other controls use opacity-50.
  • Labels: every control is tied to its label with htmlFor/id. Use sr-only labels 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: 150

Surfaces pair with a foreground

Aa

Page

Default page surface

--background
Aa

Card

Elevated panels

--card
Aa

Muted

Subdued backgrounds

--muted
Aa

Accent

Primary actions

--accent
Aa

Destructive

Danger actions

--destructive

Guidelines:

  • OKLCH color system: five primitive scales (neutral, accent, destructive, warning, success), each 11 steps (50 to 950) with perceptually uniform lightness.
  • Role, not hue. Tokens name a role (--accent, --destructive), never a color. Change --color-accent-h and every step of the scale retones together.
  • Semantic tokens resolve per mode: bg-card and text-foreground swap in :root and .dark. Surfaces flip; the accent stays constant at accent-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.