Skip to content

Reference · craft

Layout and spacing

craft/references/layout.md73 linesupdated 16 Sept 2026

1. Containers

Utility Width Use Measured band
container-page max 76rem (1216px) incl. fluid gutter 20→40px default marketing page 1180–1232 (Stripe, Resend, Clerk, Framer, Granola)
max-w-wide mx-auto px-gutter 90rem (1440px) product-dense, full-bleed galleries, instrument 1392–1408 (Vercel, Attio, Railway)
max-w-prose mx-auto px-gutter 65ch articles, docs body 640–720 (Paco 640, Emil 692)
max-w-narrow mx-auto px-gutter 40rem forms, auth, short CTA

Template: (page), , . Never nest containers. Full-bleed backgrounds go on the

, the container sits inside.

2. Vertical rhythm

  • section-y = clamp(4rem, …, 8rem) → 64px at 360, 128px at 1440. py-section is the same value as spacing.
  • Vary deliberately. Measured: Granola 112 / 128 / 160; Attio 152 top / 112 bottom; Resend uniform 96 (fine at that size). Recipe per page:
Section Classes
Hero pt-16 pb-20 sm:pt-24 lg:pt-32 lg:pb-28
Proof strip right after hero py-10 lg:py-12
Standard section section-y
Showcase / signature section py-24 lg:py-40
Final CTA band py-24 lg:py-32
Footer pt-16 pb-10
  • Inside a section: eyebrow → heading mt-4/mt-5; heading → lead mt-5/mt-6; lead → content mt-12/mt-16. Gap above a heading ≥ 2× the gap below it.
  • Mobile section padding 60–96px: section-y already clamps; don't add py-8 overrides.

3. Grids

  • 12 columns, asymmetric spans. grid gap-10 lg:grid-cols-12 with lg:col-span-7 + lg:col-span-5, or label lg:col-span-3 + content lg:col-span-9. Gap = 24–40px (gap-6gap-10).
  • Intrinsic card grid (unknown count): grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(min(18rem,100%),1fr))].
  • Hairline grid (cells separated by 1px rules, PlanetScale/hellohello): grid gap-px overflow-clip rounded-xl border border-line bg-line sm:grid-cols-2 lg:grid-cols-3 with each cell bg-bg p-6. Only when rows are fully packed; a spanning grid with holes shows the line color, so compute spans.
  • Divided list (editorial rows): divide-y divide-line-subtle border-y border-line-subtle, each row grid gap-4 py-6 lg:grid-cols-12.
  • Bento: spans follow importance (the biggest tile shows the most important real UI). Row-aligned bentos stretch small tiles; use independent columns (flex flex-col gap-4 per column) when heights differ.
  • Page shell uses media queries (sm md lg); components inside cards use container queries (@container on the parent, @md:flex-row on children).

4. Alignment

  • Left edges align across eyebrow, heading, paragraph and CTA row. Everything aligns to something.
  • Center only: a short hero lockup (≤ 3 lines H1, ≤ 2 lines sub) or a final CTA band. Measured interior headings centered: Linear 1/14, Vercel 1/18, Stripe 1/32 (design audit flags > 35%).
  • Display text optically overhangs: -ml-[0.04em] on very large H1s so the stem lines up with body text below.
  • Icon + label rows: inline-flex items-center gap-2; icon size-4 shrink-0; nudge ±1px if the glyph sits high.
  • Header: sticky top-0 z-40 h-(--header-h) (56px), border-b border-line-subtle bg-bg/92, optional supports-[backdrop-filter]:bg-bg/80 supports-[backdrop-filter]:backdrop-blur-md. Measured 56–72px. Nav links text-sm text-fg-muted hover:text-fg; CTA Button size="sm".
  • Floating inset header (Clerk/Granola top: 8px): sticky top-2 mx-auto max-w-page rounded-xl border border-line-subtle bg-bg/85 backdrop-blur-md. One or the other, not both.
  • Anchors land below the header: base sets scroll-padding-top and [id] { scroll-margin-top } from --header-h. If you change the header height, change --header-h.
  • Footer: only links that exist. 2–4 columns of text-sm, label-mono column heads, legal row text-xs text-fg-subtle. No newsletter box unless there is a newsletter.

6. Density and control sizes

Size Height Text Padding-x Use
Button size="sm" 32px (h-8) + hit-area text-sm 12px header, inline, tables
Button size="md" 36px (h-9) text-sm 14px forms, cards
Button size="lg" 44px (h-11) text-base 20px hero CTA, touch primary

Measured: nav CTAs 32px, hero CTAs 40–48px, padding 12–24px, weight 500, never bold. Inputs on mobile use ≥ 16px text (text-md) or iOS zooms. Table rows 36–40px.

7. Responsive checklist

  • Test 390 (phone), 768, 1280/1440, and 320 for overflow. No horizontal scroll at 390 (design/horizontal-overflow, a11y/reflow-overflow).
  • Grids collapse to one column below lg unless cells are tiny; keep reading order (content first, media second) with order-* only when necessary.
  • Full-height: min-h-svh, never h-screen. Flex children with media or long words: min-w-0.
  • Safe areas: container-page already includes env(safe-area-inset-left).
  • Sticky breaks under overflow: hidden ancestors; use overflow-clip.
  • Absolute decorative layers: pointer-events-none absolute inset-0 -z-10 and aria-hidden="true" on the parent section with relative isolate (or grain, which sets isolation).