Typography
The Save the Dogs type system — Mulish for body / UI, Fraunces for display — built entirely on --text-*, --lh-* and --tracking-* tokens. Flip the theme in the top bar; the type is theme-aware via the semantic color tokens.
Type system
Two faces, one token-driven scale. Body and UI copy is set in Mulish (--font-sans); display headlines opt in to Fraunces with the font-display utility. Sizes, line-heights and tracking are all CSS variables so the scale stays consistent across the app and re-skins cleanly.
The quick brown fox jumps over the lazy dog
Default face. Inherited from <body>; no class needed. Used for paragraphs, labels, controls and data.
The quick brown fox jumps over the lazy dog
Opt in with className="font-display" (or the font-display util). Reserve for hero / section headlines, not running text.
- --font-sans
- Mulish, Inter, system-ui — the body/UI face, set on <body> and inherited everywhere.
- --font-display
- Fraunces (serif). Opt in via .font-display / font-display; falls back to Georgia.
- --font-mono
- Geist Mono — used here for token names and code via the font-mono utility.
- text color
- Primary = text-[var(--color-text)]; secondary = text-muted; tertiary = text-faint. Never rely on size alone for hierarchy.
Type scale
Every --text-* step, largest to smallest, with its token, rem/px size and intended role. Heading tiers are shown in the Fraunces display face (as the app uses them); body tiers in Mulish. Each sample is captioned with its token.
--text-display-2xl3.5rem · 56px · font-displayHero / marketing display — the largest face. Pair with font-display.--text-display-xl2.75rem · 44px · font-displaySecondary hero / large display headline.--text-h12rem · 32px · font-displayPage title (PageHeader sets this in font-display).--text-h21.5rem · 24px · font-displaySection heading (DsSection title is font-display at this size).--text-h31.25rem · 20pxSub-section / card group heading.--text-h41.0625rem · 17pxCard title / dense heading (same size as body-lg).--text-body-lg1.0625rem · 17pxLead paragraph / emphasized body copy.--text-body0.875rem · 14pxDefault body text — inherited from <body>.--text-small0.8125rem · 13pxSecondary / supporting copy, captions, metadata.--text-eyebrow0.6875rem · 11pxEyebrow / overline label — uppercase + tracked out.Display vs body face
The same heading rendered in both faces at the same size, so you can see why Fraunces is the display choice and Mulish the body choice. Headlines use font-display; everything below h3 stays in Mulish.
Fraunces display
Mulish body
Fraunces display
Mulish body
Fraunces display
Mulish body
Fraunces display
Mulish body
Line-height
The --lh-* tokens. Tight leading on display/headings keeps multi-line headlines compact; the looser --lh-body is the readable default for paragraphs (and the value set on <body>).
The quick brown fox jumps over the lazy dog. Save the Dogs is dark by default; every color resolves from a CSS variable, so the same type ramp re-skins across the light and dark themes with no per-component edits.
The quick brown fox jumps over the lazy dog. Save the Dogs is dark by default; every color resolves from a CSS variable, so the same type ramp re-skins across the light and dark themes with no per-component edits.
The quick brown fox jumps over the lazy dog. Save the Dogs is dark by default; every color resolves from a CSS variable, so the same type ramp re-skins across the light and dark themes with no per-component edits.
The quick brown fox jumps over the lazy dog. Save the Dogs is dark by default; every color resolves from a CSS variable, so the same type ramp re-skins across the light and dark themes with no per-component edits.
The quick brown fox jumps over the lazy dog. Save the Dogs is dark by default; every color resolves from a CSS variable, so the same type ramp re-skins across the light and dark themes with no per-component edits.
- --lh-body
- 1.5 — body / body-lg / small.
- --lh-h1
- 1.15 — display-2xl, display-xl, h1.
- --lh-h2
- 1.25 — h2.
- --lh-h3
- 1.3 — h3.
- --lh-h4
- 1.35 — h4.
Letter-spacing (tracking)
The --tracking-* tokens. Negative values optically tighten large display text; the positive eyebrow value tracks small uppercase labels OUT for legibility. Each line below is the same string at one tracking value.
Save the Dogs design system
Save the Dogs design system
Save the Dogs design system
Track small uppercase labels out
- --tracking-tight
- -0.025em — Tightest — large display / numerals.
- --tracking-display
- -0.02em — Display faces (display-2xl, display-xl).
- --tracking-h
- -0.012em — Headings (h1–h4) — subtle optical tighten.
- --tracking-eyebrow
- 0.08em — Eyebrows / uppercase labels — track OUT.
Eyebrow & uppercase
The eyebrow pattern: --text-eyebrow size, --tracking-eyebrow spacing, uppercase, set in a faint/muted tone above a heading. Used as overlines on section cards and table headers.
Foundations
Typography
| Name | Role | Status |
|---|---|---|
| Rosa | Lead | Active |
| Marcus | Volunteer | Pending |
- --text-eyebrow
- 0.6875rem / 11px — the eyebrow font-size.
- --tracking-eyebrow
- 0.08em — tracks the uppercase label out so it reads as a label, not shouting.
- uppercase + faint
- Pair uppercase with text-faint/text-muted weight 600 — the global `th` style applies exactly this.
Tabular numerals
Opt into fixed-width figures with the tabular-nums utility so columns of numbers (metrics, tables, money) align vertically. Compare the proportional default against tabular — the digits line up on the right.
- tabular-nums
- Utility class setting font-variant-numeric: tabular-nums. Apply to any container of figures that must align.
- When to use
- Stat blocks, data tables, currency, counters, anything where digits stack in a column.