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. The scale runs from 6px (half-unit, for tight intra-component gaps) to 96px (hero breathing room).
Padding in context
Each token applied as vertical padding around a content block, so you can judge the breathing room each step provides.
Reference — Space Scale
| 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-sm-plus | 1rem | 16px | Off-scale optical step — compact component padding (button/nav-CTA horizontal padding) |
--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
The page container can be wide, but readable text should always be constrained to ~70ch. These two values operate independently — a wide layout can contain narrow prose.
Reference — 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 |
Page Structure
A standard page skeleton with spacing tokens at each level. Use this as a starting blueprint — the gaps between sections give the page its rhythm.
padding-top: --space-3xl--space-2xl between sections--space-2xlpadding-top: --space-xlReference — 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 topGrid Pattern
auto-fit with minmax produces a responsive layout without breakpoint queries. Resize your browser — the columns collapse automatically when there is not enough room.
Reference — 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, signalling containment without needing a border.
Reference — Border Radius
| 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. The system does not use pill-shaped badges.
Z-index Scale
Always reference a token — never use raw numbers. Each layer is designed to sit cleanly above the one before it. Never skip levels.
Reference — Z-index Scale
| Token | Value | Use |
|---|---|---|
--z-base | 0 | Default page content |
--z-raised | 1 | Sticky elements in flow (sub-nav tab row) |
--z-content | 10 | Sticky sub-nav — above page, below fixed nav |
--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.
Elevation
Three shadow tiers for raised surfaces — dropdowns, popovers, raised cards. Each tier is an opacity token layered under a fixed offset/blur, so dark mode can boost the alpha without redeclaring the whole shadow.
Reference — Elevation
| Token | Light opacity | Dark opacity | Use |
|---|---|---|---|
--shadow-sm | 0.06 | 0.18 | Hover rows, inline chips |
--shadow-md | 0.10 | 0.28 | Dropdowns, popovers, tooltips |
--shadow-lg | 0.14 | 0.40 | Modals, raised cards |
Dark mode needs a higher opacity for the same shadow to read against an already-dark background — override only the --shadow-opacity-* token, not the full --shadow-* value, if you need a fourth tier. --overlap-card-shadow-raised (used by .overlap-card) reuses --shadow-opacity-lg for its own upward-facing shadow.
Breakpoints
Two reference values marking layout transitions. Use min(), max(), and clamp() within components — only reach for breakpoints at the page layout level.
Reference — Breakpoints
| Token | Value | Behaviour |
|---|---|---|
--breakpoint-mobile | 640px | Single column; horizontal padding: --space-md |
--breakpoint-tablet | 768px | Single column centered; horizontal padding: --space-lg |
| (--width-content) | 1080px | Centered container, auto horizontal margins |
CSS custom properties cannot be used directly in @media queries. Use the token value as a reference comment.
/* --breakpoint-mobile: 640px */
@media (max-width: 640px) {
.layout { flex-direction: column; }
}