Spacing & scale
A 4px base unit drives a consistent spacing scale. Use 4px for tight gaps (icon-to-text) and 8px and up for structural spacing (form fields, list items, sections).
Base spacing scale
All spacing derives from --spacing: 0.25rem (4px). Bars scale in proportion to value.
| Class | Scale | Value |
|---|---|---|
| p-1 | 4px | |
| p-2 | 8px | |
| p-3 | 12px | |
| p-4 | default | 16px |
| p-5 | 20px | |
| p-6 | 24px | |
| p-8 | 32px | |
| p-12 | 48px | |
| p-16 | 64px | |
| p-20 | 80px |
Content group coupling
4px within a group, 8px between groups. Elements that read as a single unit (a title and its lead, a label and its value) sit at 4px to signal association. Anything beyond that is structure.
Tight coupling (4px)
The paragraph sits close enough to read as the same thought: one unit, one breath. This is gap-1.
gap-1 · 4px
Loose coupling (8px)
The paragraph feels separated: the right call between groups, the wrong call inside one. This is gap-2.
gap-2 · 8px
Max widths
Width constraints for containers and panels, from small popovers to full page.
| Token | Usage | Value |
|---|---|---|
| --spacing-max-w-sm | Popovers, tooltips | 320px |
| --spacing-max-w-md | Dialogs, small panels | 384px |
| --spacing-max-w-lg | Forms, medium panels | 512px |
| --spacing-max-w-xl | Content columns, prose | 672px |
| --spacing-max-w-2xl | Wide content, two-column | 896px |
| --container | Page content maximum | 1540px |
Reading measure
Line length governs reading rhythm. Caps are measured in ch, so a single value holds at every type tier. You may go narrower than a measure, never wider.
| Token | Usage | Value |
|---|---|---|
| max-w-measure | Reading copy: a 60 to 75 character measure | 60ch |
| max-w-measure-heading | Headings wrap short rather than stretch | 24ch |
Headings break into lines rather than stretch across the page.
Body copy holds a 60 to 75 character measure so the eye returns to a predictable left edge on every line. Long lines tire the eye; short ones fragment the thought. The measure is set in ch, so the physical width tracks the type size without a separate cap per tier.
Vertical rhythm
The prose-flow utility spaces long-form text: above an element the gap scales with the type, below a heading it is half, and everything floors at 16px. Headings breathe more above than below, so each belongs to what follows.
| Token | Usage | Value |
|---|---|---|
| --rhythm-base | Floor gap between any two blocks | 16px |
| --rhythm-above-h3 | Above h3 through h6 | 28px |
| --rhythm-above-h2 | Above h2 | 48px |
| --rhythm-above-h1 | Above h1 | 64px |
| --rhythm-below-h1 | Below h1 (half its above-gap) | 32px |
Trigger conditions
Each contract specifies a trigger event tied to an objective public source. The gap above this heading is large; the gap below it is half, so the heading reads as the start of this block.
Event definition
The trigger event is a measurable condition with a single binary outcome. Sub-headings take a smaller gap above than the section heading, keeping the hierarchy legible through spacing alone.
A second paragraph sits one floor-gap (16px) below the first. Body blocks never crowd and never drift.
Breakpoints
Mobile-first responsive breakpoints. Tailwind defaults, applied as min-width media queries.
| Breakpoint | Usage | Value |
|---|---|---|
| sm: | Large phones, small tablets | 640px |
| md: | Tablets, primary breakpoint | 768px |
| lg: | Small laptops | 1024px |
| xl: | Desktops | 1280px |
| 2xl: | Large desktops | 1536px |
Shadow scale
Four elevation levels, from subtle form control depth to modal-level lift.
shadow-xs
Form controls (Input, Select, Checkbox)
shadow-sm
Subtle elevation
shadow-mddefault
Cards, elevated surfaces
shadow-lg
Modals, dialogs
Border radius
A progressive scale from subtle rounding to fully circular. Each level has a defined usage.
xs
0.25rem
Close buttons, micro elements
sm
0.375rem
Checkbox, small controls
md (default)
0.5rem
Menu items, Popover, Tooltip
lg
0.75rem
Cards, dialogs, menu popups
xl
1rem
Drawers, sheets, large panels
2xl
1.25rem
Unassigned
full
9999px
Badges, pills, switches
action-xs
0.5625rem
Controls 24px tall
action-sm
0.65625rem
Controls 28px tall
action
0.75rem
Controls 32px tall
action-lg
0.9375rem
Controls 40px tall
Z-index scale
Four rungs, no named tokens. Every overlay shares z-50 on purpose: Base UI portals them to the body in mount order, so the last one opened wins. That is what lets a Select inside a Dialog sit above it, and a Dialog opened from a menu sit above the menu. Never give an overlay its own z-index.
| Utility | Description | Value |
|---|---|---|
| z-10 | Elevation inside one component | 10 |
| z-20 | Sticky region in a scroll container | 20 |
| z-40 | Page chrome (SiteHeader) | 40 |
| z-50 | Anything portaled to the body | 50 |