Theming
A theme here is seventeen custom properties and a light/dark policy. The manifest names two of them:
[theme]
mode = "auto" # light (default) | dark | auto
[theme.palette]
primary = "#16213a" # body text, headings, the site title
accent = "#2f6feb" # links, focus rings, the current item in a TOC
Those two are the ones a site actually wants to change. The rest are derived or defaulted, and a site that disagrees with a derivation ships its own stylesheet rather than growing this table seventeen keys at a time.
What gets derived
accent produces its own hover shade through darken, and its dark-mode counterpart through lighten. The dark palette is not the light one inverted — inverting a page turns its hairline rules into glaring ones. Instead surfaces lift, text softens off pure white (which vibrates on a dark field), and the accent brightens, because a mid-blue that reads as a link on white disappears on near-black.
The tokens themselves are grouped by what they are for, not by colour: text / textMuted / textFaint, surface / surfaceAlt / field, border / borderStrong / rule, plus chip, highlight, shadow and scrim.
Where it ends up
Two places, and the split is deliberate.
The tokens are written inline into each page’s <head>:
<style>:root { --text: #16213a; --accent: #2f6feb; … }</style>
Inline because they are per-site data on a per-site page. A generated token file would have to be written, hashed, and linked in the right order relative to the sheets that consume it — and every one of those steps happens after the pages that depend on it were rendered. Eight hundred bytes in a <head> has no ordering problem at all.
The base stylesheet, which is the same for every site, is written once to static/theme-<hash>.css. The hash is of its own text, so an old build’s copy under a different name is stale rather than wrong.
mode = "light" emits one block and no media query. A site that has decided it is a light site should not ship rules a reader’s system setting can switch on. color-scheme rides along with the tokens: it is what tells the browser which way to render the things a stylesheet does not own — form controls, scrollbars, and the canvas behind a short page.
That last one is not hypothetical. An early version set colours but never a background, so a light palette rendered onto a dark-mode browser’s dark canvas: dark text on a dark page, in a build that reported success. Only a screenshot caught it.
Your own stylesheet
Drop a file at content/static/style.css and it is linked after the built-in theme:
<link rel="stylesheet" href="/static/theme-768d7ac7.css">
<link rel="stylesheet" href="/static/style.css?v=…">
Order is the whole override mechanism. Later wins, so a site customises by adding rules rather than by replacing a theme — and it can read the tokens while doing it:
.article-card { border-left: 3px solid var(--accent); }
The site sheet is content-hashed in its URL too, so browsers re-fetch it exactly when it changes and not one reload later.