foundations
Spacing and layout
Every spatial value is a multiple of 4px. Picked names, fixed scales. No 3px, 5px, 7px — ever.
Read the full specThe 4-px ruler
Each row is a spacing token at its true pixel width, rendered against a background 4-px grid.
- 0.5· 2pxInline gap between icon and label
- 1· 4pxTight UI — badge padding Y
- 2· 8pxForm-field row-gap
- 3· 12pxSmall gap between related elements
- 4· 16pxDefault — cards, stacks
- 5· 20pxBetween UI clusters
- 6· 24pxSection-within-page gap
- 8· 32pxBetween major sections
- 12· 48pxComfortable hero padding
- 16· 64pxLanding-page section padding
- 24· 96pxMaximum regular section padding
Container widths
Named max-widths. Never arbitrary percentage widths like w-[75%] — they scale with viewport, not content.
- max-w-prose720px · 65chLong-form reading
- max-w-narrow-form480pxSingle-column forms
- max-w-md768pxDefault article width
- max-w-lg1024pxDashboards, most app content
- max-w-xl1280pxWide dashboards, marketing sections
- max-w-2xl1536pxMarketing hero, full-bleed
<main className="mx-auto max-w-xl px-6">
{ /* Wide dashboards, marketing sections */ }
</main>Breakpoints — mobile-first
Every breakpoint is 'at this width and above'. Design mobile-first; progressively enhance.
- base0–639pxPhone (portrait)
- sm≥640pxLarge phone, narrow tablet
- md≥768pxTablet portrait
- lg≥1024pxTablet landscape, small laptop
- xl≥1280pxLaptop, small desktop
- 2xl≥1536pxDesktop, large monitor
Density — comfortable vs compact
Two modes. Marketing and most product UI use comfortable. Data-dense surfaces opt into compact.
Comfortable
- Midrand North47
- Johannesburg CBD128
- Khayelitsha63
- Sandton19
row-height: 48px · input-height: 40px
Compact
- Midrand North47
- Johannesburg CBD128
- Khayelitsha63
- Sandton19
row-height: 36px · input-height: 32px
Stack rhythm
Inside a card: p-6, space-y-4. Between cards: gap-4 on small, gap-6 on medium+. Between sections: py-12–py-24.
Card A
Internal stack uses space-y-3 between tight elements.
Card B
Between cards uses gap-6 — one step above internal rhythm.
<section className="py-16">
<div className="mx-auto max-w-xl px-6">
<div className="flex flex-col gap-6">
{ /* cards */ }
</div>
</div>
</section>