⚠️ Design system CSS not found. Check the path in cms/docs.config.js → designSystemPath, then re-run npm run docgen.

Typography

The type system as web tokens

Website / Typography
Download .md file
Open .md in new tab
On this page
  • Font slots
  • Web hierarchy
  • Where the web differs from the design file
  • Loading

The brand typefaces are loaded once and assigned to the design system's font slots. The type scale itself — the sizes, the leading, the breakpoints at which headings step down — is the engine's and is not a brand decision; the brand supplies the faces and says which slot each fills.

Font slots

Slot Face Loaded by
Inter A Google Fonts link the generator emits from brand.json
Erode An @font-face rule in theme.css pointing at assets/fonts/, preloaded from brand.json
Inter Aliased to in theme.css, because the engine's badge reads it
engine default (a system monospace stack) Not a brand slot; still dresses every code, pre, kbd and token tag
, , engine defaults Not brand slots; nothing on the site uses them

Headings read --title-family, which the theme points at --font-secondary, so every h1 to h6 renders in Erode. Body text and interface read --font-primary, with three engine exceptions: pull quotes and drop caps read --font-secondary (a blockquote is Erode italic, the one place the italic file loads), badges read --font-tertiary, and code reads --font-quaternary. Page code references the slots, never a family name.

Web hierarchy

Sizes are stepped rem values, shown here in pixels at desktop and at 959 px and below, where the headings step down one size.

Element Size Leading
h1 48 → 36 1.2
h2 40 → 32 1.2
h3 36 → 28 1.2
h4 32 → 24 1.2
h5 28 → 22 1.2
h6 24 → 20 1.4
Body 16 1.6
Lead () 20
Small () 14
14, uppercase, tracked 0.12em 1.2

Type follows browser zoom and the reader's font-size preference because every step is a rem value; there is no viewport-relative type, and no page writes its own breakpoint typography.

Where the web differs from the design file

  • Body is 16 px on the web, 20 px in the design file. The 20 px setting is the lead paragraph, --text-size-l.
  • Size names do not line up. The design file calls 20 px font-m and 24 px font-l; the engine's --font-m is 18 px and --font-l is 20 px. Read the pixel value in an inspect panel, never the name.
  • Eyebrow tracking is narrower on the web. The design file tracks the 18 px eyebrow by 8 px (0.44em); renders at 14 px with 0.12em, because wider tracking wraps uppercase text at small sizes.
  • The 94 px display size has no engine step. The largest is --font-10xl at 72 px, reached through a display class rather than a heading element.

Loading

Inter arrives from Google Fonts through the googleFontsUrl in cms/blackdoctor/brand.json; the generator writes the link into every page head. Erode is self-hosted: the file sits in assets/fonts/, theme.css declares it with @font-face, and brand.json lists it under fontPreload so the browser fetches it before the stylesheet asks for it. The fallback stack is Georgia, so a page reads as a serif heading over sans text even before the file lands.

On this page
  • Font slots
  • Web hierarchy
  • Where the web differs from the design file
  • Loading
Previous Colour
Next Social Instagram

© 2026 BlackDoctor