Spacing
Proximity signals relationship; distance signals importance. The spacing scale encodes this — small gaps within components, large gaps between sections.
Space Scale
Base unit is 12px. All tokens are multiples of 12, except --space-xs (6px = half-unit).
| Token | rem | px | Use |
|---|---|---|---|
--space-xs | 0.375rem | 6px | Icon-to-label gap, tag padding |
--space-sm | 0.75rem | 12px | Tight grouping within a component |
--space-md | 1.5rem | 24px | Standard gap — when in doubt, start here |
--space-lg | 2.25rem | 36px | Between distinct content blocks |
--space-xl | 3rem | 48px | Section breathing room, major transitions |
--space-2xl | 3.75rem | 60px | Between page sections |
--space-3xl | 4.5rem | 72px | Hero padding, top-of-page breathing room |
--space-4xl | 6rem | 96px | Maximum section gap — use sparingly |
Usage Rules
- xs / sm — intra-component only. Never use to separate independent elements.
- md — the default gap. When in doubt, start here.
- lg / xl — between content blocks within a section.
- 2xl / 3xl — between named page sections. The visual pause that lets sections breathe.
- 4xl — reserved for hero areas. More than one use per page usually signals restructuring is needed.
Layout Widths
| Token | Value | Use |
|---|---|---|
--width-content | 1080px | Outer page container |
--width-prose | 70ch | Body copy, articles, long-form text |
--width-narrow | 640px | Forms, focused UI, modals |
The page container can be wide (--width-content), but readable text should always
be constrained to ~70ch (--width-prose). These two values operate independently.
Page Structure
[site header / nav] — sticky or static, full-width
[hero / page header] — padding-top: --space-3xl
[content sections] — gap between sections: --space-2xl
[section heading]
[section body] — max-width: --width-prose or --width-content
[site footer] — padding: --space-xl top Grid Pattern
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--space-md); /* 24px */
} Use CSS Grid for two-dimensional layouts, flexbox for one-dimensional. Never go beyond 3 columns at --width-content.
Border Radius
Each radius maps to a specific element type. The progression creates implicit hierarchy — larger surfaces have larger radii.
| Token | Value | Use |
|---|---|---|
--radius-sm | 4px | Badges, tags, nav link focus ring, checkboxes |
--radius-md | 6px | Buttons, inputs, selects, textarea, icon containers |
--radius-lg | 8px | Cards, dropdowns, popovers, drawers |
--radius-xl | 12px | Large panels, featured sections |
--radius-full | 9999px | Avatar circles (one-off use — not for badges) |
A badge (4px) sits inside a card (8px) — the radius difference signals containment without needing a border. The system does not use pill-shaped badges.
Z-index Scale
Always reference a token — never use raw numbers. Never skip levels.
| Token | Value | Use |
|---|---|---|
--z-base | 0 | Default page content |
--z-raised | 1 | Sticky elements in flow (sub-nav tab row) |
--z-dropdown | 100 | Dropdowns, popovers |
--z-sticky | 200 | Sticky nav bar, fixed header |
--z-overlay | 300 | Overlays, mobile nav drawer |
--z-modal | 400 | Modals |
--z-toast | 500 | Toast notifications |
The nav (--z-sticky: 200) sits below overlays (--z-overlay: 300) — dropdowns triggered inside the nav render correctly above it without adjustment.
Responsive Breakpoints
| Breakpoint | Behaviour |
|---|---|
| Mobile < 640px | Single column. Horizontal padding: --space-md (24px) |
| Tablet 640–1079px | Single column, centered. Horizontal padding: --space-lg (36px) |
| Desktop ≥ 1080px | Centered container at --width-content, auto horizontal margins |
Avoid fixed pixel breakpoints inside components — use min(), max(), and clamp() where the content dictates the reflow point.