Spacing & grid

Everything on an 8px base grid. Spacing tokens are multiples of 4px — the minimum step — with 8px as the workhorse. The grid is 12 columns with 16px gutters, 32px margins, and a 1240px max-width container. Radius tokens match the component scale: small radius for small things, pill for CTAs.

Spacing scale

The scale starts at 4px and doubles in familiar increments. Steps 2 (8px), 4 (16px), 6 (24px), and 8 (32px) are the most commonly used — they form the inner rhythm of cards, forms, and list items. Steps above 16 (64px) are layout-level gaps between sections.

sp-0 0px --sp-0
sp-1 4px --sp-1 Icon gap, fine-tune nudges
sp-2 8px --sp-2 Inline gap, label-to-input
sp-3 12px --sp-3 Tight stacks, small card padding
sp-4 16px --sp-4 Default element gap, list item padding
sp-5 20px --sp-5 Card inner padding (mobile)
sp-6 24px --sp-6 Card inner padding (desktop), section gap
sp-8 32px --sp-8 Page margins, modal padding
sp-10 40px --sp-10 Between cards in a stack
sp-12 48px --sp-12 Top of main content
sp-16 64px --sp-16 Between page sections
sp-20 80px --sp-20 Major section breaks
sp-24 96px --sp-24 Hero padding, large section gap

12-column grid

The grid is 12 columns with 16px gutters and 32px page margins, capped at 1240px. On mobile (below 520px), margins shrink to 20px and the grid collapses to 4 columns. On tablet (520–900px), 8 columns are available. The 12-column system divides cleanly into halves, thirds, quarters, and sixths — all the splits a friend group needs.

12 columns · 16px gutters · 32px margins · 1240px max-width
1
2
3
4
5
6
7
8
9
10
11
12
6 col — half
6 col — half
4 col — third
4 col — third
4 col — third
3 col
3 col
3 col
3 col
8 col — main
4 col — aside
/* Container */
.container {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 32px;  /* --grid-margin */
}
@media (max-width: 520px) {
  .container { padding: 0 20px; }  /* --grid-margin-mobile */
}

/* 12-column CSS grid */
.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--grid-gutter);  /* 16px */
}

/* Responsive collapse */
@media (max-width: 720px) {
  .grid { grid-template-columns: repeat(8, 1fr); }
}
@media (max-width: 520px) {
  .grid { grid-template-columns: repeat(4, 1fr); }
}

Breakpoints

Four breakpoints, named for device classes not pixel values — so they stay meaningful when device categories shift. Mobile-first: start at full width, add constraints as the viewport grows. The 900px breakpoint is where the sidebar navigation appears and single-column stacks become two-column grids.

0 — mobile
520px — sm
720px — md
900px — lg
1240px — xl
NameValueTokenBehavior
sm520px--bp-sm4 → 8 columns, mobile nav visible, single-column cards
md720px--bp-md8 columns, full grid starts, some 2-column layouts
lg900px--bp-lgSidebar nav appears, 12 full columns, content max-width in effect
xl1240px--bp-xlContainer max-width reached, centered layout

Radius scale

Radius scales with component size. Small components get small corners. Cards get the generous lg radius. Pill is for CTAs and nav items where full rounding creates a distinct button language. Never mix large radius on small components — a 24px corner on a 13px tag looks bloated.

xs6px
--r-xs
Tags, inner pills
sm10px
--r-sm
Chips, mini inputs
md14px
--r-md
Inner cards, inputs
lg24px
--r-lg
Cards, sheets
xl28px
--r-xl
Composer, settle card
pill999px
--r-pill
Nav pills, CTAs

Z-index scale

The z-index scale is named, not arbitrary. Using raw numbers like z-index: 9999 creates stacking order bugs that are invisible until a tooltip appears under a modal. Always use the named tokens.

TokenValueUsed for
--z-base0Default document flow
--z-card1Elevated cards, hover shadows
--z-sticky10Sticky headers, pinned rows
--z-nav50Tab bar, floating nav
--z-modal100Modals, sheets, drawers
--z-toast200Toast notifications — above modals
--z-tooltip300Tooltips — above everything interactive
--z-grain1000Paper grain overlay — always topmost, pointer-events:none

Usage

Do Snap all spacing to the scale. If you need 10px, use 8px or 12px. Off-grid spacing is visible as inconsistency even when viewers can't name the problem — they feel the roughness.
Don't Don't use arbitrary spacing values (10px, 14px, 18px, 22px). Don't mix px and rem for layout — pick px and stay consistent. Don't nest containers inside containers with their own margins.
Do Use radius tokens that match the component size. Tags get --r-xs. Cards get --r-lg. The settle CTA button gets --r-pill. Scale radius with component scale.
Don't Don't use border-radius: 8px on a full-screen modal — it clips the corners visibly on mobile. Don't use --r-lg on a small chip — it makes the radius larger than the element's own height.
Do Use named z-index tokens. If you find yourself needing a z-index above 1000, you have a stacking context problem — fix the structure, don't pile on.
Don't Don't hardcode z-index values (z-index: 999, z-index: 9999). They always conflict eventually and the bug surfaces in the worst moment — a modal hiding behind a sticky header on a customer's device.