Typography
Display serif for personality, humanist sans for readability, mono for code. The combination creates warmth without sacrificing legibility.
Font Stacks
| Token | Stack | Role |
--font-display | 'Fraunces', Georgia, serif | Headings h1–h3, display copy |
--font-body | 'Instrument Sans', system-ui, sans-serif | Body copy, paragraphs |
--font-mono | 'JetBrains Mono', 'Courier New', monospace | Code, <pre> blocks |
Fraunces is an optical-size variable serif — warm and considered rather than generic.
Instrument Sans pairs naturally: geometric enough to feel clean, humanist enough to not
feel cold. Together they create the “familiar but differentiated” quality the system aims for.
Fraunces Optical Size — Critical Rule
Fraunces is a variable font with an optical size axis (opsz).
This axis must always be set explicitly — the font renders
differently at different optical sizes and will look wrong without it.
| Element | opsz value | Why |
h1 | 72 | Optimises letterform detail for large display sizes |
h2 | 24 | Section heading weight — balanced stroke contrast |
h3 | 20 | Subsection — slightly thicker strokes at smaller size |
h1 {
font-family: var(--font-display);
font-weight: 800;
font-variation-settings: 'opsz' 72; /* mandatory */
}
h2 {
font-family: var(--font-display);
font-weight: 700;
font-variation-settings: 'opsz' 24; /* mandatory */
}
h3 {
font-family: var(--font-display);
font-weight: 600;
font-variation-settings: 'opsz' 20; /* mandatory */
}
Type Scale
A modular scale anchored at 16px base, stepping ~1.25× (major third).
| rem | px | Use |
0.75rem | 12px | Captions, labels, badges |
0.875rem | 14px | Small body, UI secondary |
1rem | 16px | Default body |
1.125rem | 18px | Large body, intro copy |
1.25rem | 20px | H3 base |
1.5rem | 24px | H2 base |
2.25rem | 36px | H1 base |
4.5rem | 72px | Display / hero |
Use clamp() for fluid heading sizes — no breakpoint jumps. The DocLayout already applies clamp() to all h1–h3 elements.
Heading Styles
| Element | Font | Weight | Size | opsz | Tracking |
h1 | Fraunces | 800 | clamp(2.5rem, 5vw, 4rem) | 72 | -0.02em |
h2 | Fraunces | 700 | clamp(1.5rem, 3vw, 2rem) | 24 | -0.02em |
h3 | Fraunces | 600 | clamp(1.1rem, 2vw, 1.35rem) | 20 | -0.01em |
h4 | Instrument Sans | 600 | 1rem | — | 0 |
h5, h6 | Instrument Sans | 600 | 0.875rem | — | 0.04em |
Body & UI Styles
| Role | Font | Weight | Size | Line height |
| Body | Instrument Sans | 400 | 16px | 1.7 |
| Body large | Instrument Sans | 400 | 18px | 1.7 |
| Body small | Instrument Sans | 400 | 14px | 1.7 |
| Label | Instrument Sans | 600 | 12px | — |
| Caption | Instrument Sans | 400 | 12px | 1.6 |
| UI / buttons | Instrument Sans | 500–600 | 13–14px | 1.6 |
| Code | JetBrains Mono | 400–500 | 13px | 1.6 |
Letter Spacing
| Value | Use |
-0.02em | Display, h1, h2 — tightens large headings |
0em | Default for body and most UI |
0.04em | H5/h6, small UI labels |
0.12em | Uppercase caps, overline labels |
Responsive Typography
| Breakpoint | H1 | H2 | Body |
| Mobile < 640px | 28px | 22px | 16px |
| Tablet 640–1079px | clamp(28px, 5vw, 52px) | clamp(22px, 4vw, 36px) | 16px |
| Desktop ≥ 1080px | 36px | 24px | 16px |
Loading Fonts
Load via a <link> tag in <head> — not via @import url() inside CSS. The import approach delays rendering.
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Instrument+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"
/>
Usage Rules
- Fraunces for h1–h3 and display copy only — never for body text, UI labels, or navigation.
font-variation-settings: 'opsz' is mandatory on every Fraunces usage: h1 → 72, h2 → 24, h3 → 20. - Instrument Sans for everything interactive: buttons, inputs, nav, labels, UI text.
- JetBrains Mono for code and inline code only — never decorative.
- Body copy max-width: 70ch (
--width-prose). Never use Instrument Sans at weight 700+. - Fraunces minimum size: 16px — never smaller.