Skip to content
ZenithDocs
English

Typography

Compare font pairings, and set your own.

On this page

ZenithDocs self-hosts its fonts through the Astro Fonts API: nothing is fetched from a third party at runtime, and no layout shift is paid for it. Seven presets are available through the fonts option, and only the one you pick is downloaded.

astro.config.mjs
zenith({
  title: 'My Docs',
  fonts: 'literata',
});

Try a preset

The buttons below swap the fonts of this whole page, chrome included, so you can judge a preset in place rather than on a specimen sheet. Scroll around after switching.

They work by setting --font-body, --font-mono and --font-display, which is exactly what you would write in a customCss file to use a family of your own.

Preset Headings Body Code
instrument Instrument Serif Instrument Sans Martian Mono
geist Geist Geist Geist Mono
newsreader Newsreader Onest IBM Plex Mono
schibsted Schibsted Grotesk Schibsted Grotesk JetBrains Mono
space Space Grotesk Space Grotesk Space Mono
atkinson Atkinson Hyperlegible Atkinson Hyperlegible JetBrains Mono
literata Literata Literata IBM Plex Mono

The presets with a serif use it for h1 and h2 only. Lumos keeps one family token per heading level, so h3 and below stay on the body font, where a high-contrast serif would become hard to read at small sizes.

A heading three, to judge the smaller sizes

A paragraph of running text, long enough to show how the pairing behaves over several lines: the width of the lowercase, the shape of the numerals like 0123456789, and how it handles inline code and a link in the middle of a sentence.

sample.ts
export function greet(name: string, excited = false): string {
  return `Hello ${name}${excited ? '!' : '.'}`;
}

Setting your own

Three tokens decide the families, whatever the preset. Set them in a customCss file:

src/styles/fonts.css
:root {
  --font-body: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --font-display: 'Playfair Display', serif;
}
Token Used for
--font-body Body text, the interface and headings from h3 down
--font-mono Code blocks and inline code
--font-display h1 and h2, falling back to --font-body when unset

A token only names a family: the font itself still has to reach the browser. In an Astro project, declare it with the Astro Fonts API to have it self-hosted, load it from the head slot, and point the token at its CSS variable:

Declare the font

astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';
import zenith from 'zenith-docs';

export default defineConfig({
  fonts: [
    { provider: fontProviders.fontsource(), name: 'Inter', cssVariable: '--font-inter' },
  ],
  integrations: [
    zenith({
      title: 'My Docs',
      fonts: false,
      customCss: ['./src/styles/fonts.css'],
      slots: { head: './src/components/Fonts.astro' },
    }),
  ],
});

Load it on every page

src/components/Fonts.astro
---
import { Font } from 'astro:assets';
---

<Font cssVariable="--font-inter" preload />

Use it

src/styles/fonts.css
:root {
  --font-body: var(--font-inter);
}

With the CLI there is no Astro configuration to declare fonts in: stick to a preset, to fonts installed on the reader’s system, or to an @font-face rule of your own in the stylesheet.

fonts: false ships no font at all. Without tokens of your own, text then uses the system font stack.

Sizes

Heading sizes are fluid: each level scales between a -min and a -max value, in pixels, with the width of the viewport. To make everything larger or smaller at once, change the two scales rather than each size:

:root {
  --zd-heading-scale: 1.15; /* h1 to h6 */
  --zd-text-scale: 1.05; /* body text */
}

A single level can also be set on its own, in which case the scale no longer applies to it:

:root {
  --h1-min: 32;
  --h1-max: 40;
}

Last updated on