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.
zenith({
title: 'My Docs',
fonts: 'literata',
});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 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.
export function greet(name: string, excited = false): string {
return `Hello ${name}${excited ? '!' : '.'}`;
}Three tokens decide the families, whatever the preset. Set them in a customCss file:
: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:
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' },
}),
],
});---
import { Font } from 'astro:assets';
---
<Font cssVariable="--font-inter" preload />: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.
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;
}