Theming

Theming

The site's colors are a small set of semantic token pairs with light and dark variants. A deployment overrides them with an optional theme.json; the result is merged onto the base palette and contrast-validated at build time.

Tokens

Each semantic role has a background (-bg) and foreground (-fg) token, in both light and dark modes. The theme.json keys are the token names without the leading --:

{
    "light": {
        "page-bg": "#faf7ff",
        "page-fg": "#1e1b2e",
        "primary-bg": "#ede9fe",
        "primary-fg": "#5b21b6",
        "accent-bg": "#fff3e0",
        "accent-fg": "#8a4b00"
    },
    "dark": {
        "page-bg": "#14121f",
        "page-fg": "#ece9f6",
        "primary-bg": "#2a2140",
        "primary-fg": "#c9b6ff",
        "accent-bg": "#3a2b08",
        "accent-fg": "#f3c968"
    }
}
json

The full semantic set covers page, primary, accent, success, warning, and error — each with a -bg/-fg pair. theme.json only needs to include the tokens you want to change; the rest come from the base palette.

Contrast validation

The design rule is simple: every -fg must be legible on its -bg. The merged palette is checked at build time, so a low-contrast override fails the build rather than shipping an unreadable site. Tokens are emitted as CSS custom properties and consumed throughout the app.

Light / dark at runtime

Viewers get a color-mode selector (auto / light / dark) in the header, persisted in a cookie — which is why every token is defined for both modes.

The palette above is the example theme that ships in the repo's dev-config, and it's also the source of this docs site's violet + amber accents.