← Visual design
toolshed

Spacing and typography

The spacing scale, font stack, heading sizes, and label/meta typography conventions used across every component.

Spacing scale

Seven steps, defined as CSS custom properties in src/styles/tokens.css:

TokenValuePixels
--space-xs0.25rem4px
--space-sm0.5rem8px
--space-md1rem16px
--space-lg1.5rem24px
--space-xl2rem32px
--space-2xl3rem48px
--space-3xl4rem64px

Components use tokens exclusively. No arbitrary pixel values in component CSS.

Font stack

Three families, all self-hosted as variable fonts:

RoleFamilyFallbacks
HeadingsLora (serif)Georgia, Times New Roman
BodyRoboto (sans-serif)-apple-system, system-ui
CodeJetBrains MonoMenlo, Monaco, Consolas

Referenced as var(--font-heading), var(--font-body), var(--font-mono).

Heading scale

Fluid sizing with clamp() so headings scale between viewport widths:

ElementSize
h1clamp(2rem, 4vw, 2.75rem)
h2clamp(1.5rem, 3vw, 2rem)
h3clamp(1.25rem, 2.5vw, 1.5rem)

All headings: line-height: 1.2, font-weight: 600.

Post titles use a separate scale: clamp(1.8rem, 4vw, 2.5rem).

Body text

  • Base font-size: 1rem (browser default 16px)
  • Line-height: 1.7 β€” generous for comfortable reading
  • Prose max-width: 42rem β€” keeps line lengths readable

Label and meta typography

A consistent sub-scale is used for labels, tags, collection identifiers, and secondary metadata throughout all components. These rules must stay consistent to maintain visual hierarchy.

UseSizeWeightTransformLetter-spacing
Collection label (card top)0.62rem700uppercase0.08em
Maturity / AI label0.58rem600uppercase0.06em
Section headings (toolshed, filters)0.65–0.72rem700uppercase0.1–0.12em
Sidebar section heading0.72rem600uppercase0.03em
Card dates, muted meta0.62–0.68rem400nonenone
Tag pills0.75rem400nonenone

The line-height trap

When mixing element types in one visually unified component (<button>, <label>, <span>), always pin line-height: 1 explicitly on the shared class. Buttons default to line-height: normal (~1.2) while spans inherit body (~1.7). Identical padding will produce different heights without this fix. See pill-bar for a concrete example.

Container widths

PurposeValue
Max page width65rem
Content/prose width42rem
Post sidebar column14rem
Mycelium tags, relations & arguments