Your docs should feel like part of your product, not a generic template. The theme block in docs.json controls that first impression: typography, accent colors, and how the site looks when readers switch between light and dark mode.
docs.page themes are CSS-variable based. When a reader opens your site, the renderer loads a base palette, optionally applies a shadcn/ui preset, then merges any custom hex colors you declare. Components (sidebar, callouts, buttons, code blocks) read from the same variables, so a small config change updates the whole site.
Everything lives under the top-level theme key in docs.json. You do not edit hosted CSS files or redeploy a theme package; commit changes to your GitHub repo and the live site picks them up on the next request.
"theme": {
"preset": "bcj0c6sa",
"primary": "#E69135"
}Field names, types, and defaults live in docs.json: this page explains the model, not every key.
A preset is a short code from the shadcn/ui theme registry. When you set theme.preset, docs.page resolves that code while parsing docs.json into a bundle of CSS: color tokens for light and dark mode, border radii, and font pairings for body and heading text.
Presets are the fastest way to get a cohesive look. Pick a code on ui.shadcn.com, drop it into docs.json, and preview with docs preview or a branch preview before you merge.
If the code is invalid or cannot be resolved, docs.page falls back to the default site palette. Other theme fields still apply.
Every hosted site supports light and dark appearance. docs.page toggles modes with a dark class on the document root (via next-themes). Preset CSS defines separate token values for :root and .dark, so backgrounds, borders, and text stay balanced in both modes.
Readers switch modes from the theme toggle in the site header. The header schema also defines showThemeToggle, but that field is schema only: the toggle always renders on hosted sites today. See Header and docs.json: Schema-only fields.
The theme block accepts defaultTheme (light or dark) to pin the starting mode. That field is also schema only: readers still get the platform default until default-theme support ships. Mode-specific color overrides apply only in the matching mode.
Beyond a preset, you can override individual tokens with six-digit hex colors (#RRGGBB, with or without the leading #).
Use primary for accent color across links, buttons, and other interactive elements. When light and dark modes need different accents, set primaryLight and primaryDark; each falls back to primary when omitted. Set backgroundLight and backgroundDark to adjust page background per mode.
Invalid hex strings are ignored rather than breaking the site. Custom colors inject as CSS overrides on top of the preset (or the default palette when no preset is set), so you can keep a preset's typography while nudging brand orange into --primary.
Typography comes from the preset when you set theme.preset. Each preset selects a sans font for body text and a heading font for titles; docs.page maps those choices onto CSS variables (--font-sans, --font-heading) and loads the matching Google Fonts faces at runtime.
The platform maintains a fixed registry of supported families (Inter, Geist, Lexend, Instrument Sans, JetBrains Mono, and others), so preset font names always resolve to web-safe files. You do not configure font keys directly in docs.json; they travel with the preset code.
To change fonts, switch presets or pick a different code on shadcn/ui that pairs the typefaces you want.
