A theme bundles layout decisions (header rows, tab position, sidebar style, breadcrumb style) with CSS tokens (colors, typography, spacing, radius). You pick a theme by slug in nookdocs.config.json#theme, then override any piece with colors, background, typography, and appearance. Dark mode is automatic — every token has a light + dark value.
{
"$schema": "https://nookdocs.com/schema.json",
"name": "Acme Docs",
"theme": "cedar",
"colors": {
"primary": "#0ea5e9",
"logo": { "light": "/logo/light.svg", "dark": "/logo/dark.svg" }
},
"appearance": { "default": "system" }
}Theme catalog
Ten presets ship out of the box. Each is a ThemeDefinition at src/lib/theme/themes/<slug>.ts that wires layout knobs to the shared token base.
| Slug | Name | Best for | Layout highlights |
cedar | Cedar | Developer docs with many tabs | 2-row header, underline tabs, header search bar |
obsidian | Obsidian | Dev-tools with a dark brand | Dark-first, sharp edges, no decoration — Linear-inspired |
larch | Larch (default) | AI / SaaS products | Sidebar-first warm autumn, amber accents — tabs listed in the rail |
ember | Ember | Fast-moving startups | Pill navigation, lightweight chrome |
birch | Birch | Enterprise docs | Deep customization, institutional feel |
sage | Sage | Marketing-adjacent docs | Card-based minimal, teal tones |
slate | Slate | Dashboard-linked docs | Headerless — UPPERCASE mono labels, tabs collapse into one rail dropdown |
oak | Oak | AI products with warm brand | Warm golden accents, modern |
cypress | Cypress | Long-form content docs | Serif headings, magazine feel |
terminal | Terminal | Hacker / CLI tools | Monospace everywhere, retro terminal |
Backward-compatible aliases also resolve: maple → larch (renamed 2026-05-05), classic → cedar, modern → ember, minimal → sage.
Where your navigation lands
The theme decides where each piece of chrome sits. Same navigation array, same
navbar config — different placement:
| Most themes | larch | slate | |
| Tabs | strip across the header | listed down the rail | one dropdown at the top of the rail |
| Search | in the header | in the rail | in the rail |
| Appearance / language | header | rail footer | rail footer |
| Version / product | header | rail footer | rail footer |
Nothing here is fixed. navbar.controls moves any control to "header" or
"sidebar" regardless of the theme — in the dashboard under Configurations →
Site → Navbar, where each control has a Theme default option that writes
nothing to your config. See
Site settings.
On narrow screens and phones every theme behaves the same way: the rail and the tab strip step aside, and search, the assistant and a breadcrumb appear in a row beside the menu button.
theme'cedar' | 'obsidian' | 'birch' | 'oak' | 'cypress' | 'ember' | 'larch' | 'sage' | 'slate' | 'terminal'pathdefault: cedarBuilt-in theme slug. Determines the shell layout (header rows, tab position, sidebar style) and the default token palette. Override individual tokens via colors, background, typography.
Appearance (light / dark)
Controls the default color-scheme and whether visitors can toggle it.
appearance.default'light' | 'dark' | 'system'pathdefault: systemInitial color scheme a visitor sees before they toggle. system honors their OS preference via prefers-color-scheme.
appearance.strictbooleanpathdefault: falseWhen true, hides the theme toggle entirely and forces a single mode. Use for brand-mandated single-mode sites. Docs still render both palettes so previews in the editor work.
Brand colors
Four overrides layer on top of the active theme's token palette. Leave any of them unset to inherit from the theme.
colors.primarystringpathPrimary accent color — buttons, links, focus rings. Hex only (e.g. #0ea5e9). Used when colors.light / colors.dark are omitted.
colors.lightstringpathPrimary accent applied in light mode. Takes precedence over colors.primary.
colors.darkstringpathPrimary accent applied in dark mode. Takes precedence over colors.primary.
colors.logo.lightstringpathLogo path (repo-relative, /logo/light.svg) or absolute URL for light mode.
colors.logo.darkstringpathLogo path or URL for dark mode. Falls back to colors.logo.light when omitted.
colors.logo.hrefstringpathdefault: /Click target for the logo. Use when the docs shouldn't route back to /.
Background
Each theme ships its own background. Cedar paints a spotlight gradient, Obsidian a near-black #09090b, Terminal a warm cream — you get the theme's own tone automatically. background is an optional override: set it only when you want something other than the theme default.
Omit
background→ the active theme's background is used.Set
background.lightand/orbackground.dark→ that mode is overridden; the other mode still inherits the theme. Partial overrides (dark-only, light-only) are fully supported.
Solid colors or CSS gradients both work — anything the browser accepts for background, including linear-gradient(...), radial-gradient(...), and CSS custom properties.
background.lightstringpathOverride the light-mode page background. Example: "#f9fafb" or "linear-gradient(180deg, #fafafa, #f3f4f6)". Omit to inherit the theme's light background.
background.darkstringpathOverride the dark-mode page background. Example: "#0a0a0a" or "radial-gradient(at top, #111827, #030712)". Omit to inherit the theme's dark background.
Because overrides are opt-in, your config stays minimal and theme background updates flow through automatically — if you never set background, switching themes always gives you the new theme's tone.
Typography
Emitted as CSS custom properties (--docs-font-sans, --docs-font-heading, --docs-font-body) and read by every theme. Load the font files yourself (Google Fonts <link>, @font-face in custom-scripts, or a system-font stack).
typography.fontFamilystringpathDefault sans-serif font — used when headingFont / bodyFont are omitted. Sets --docs-font-sans.
typography.headingFontstringpathFont stack applied to h1–h4. Falls through to fontFamily when omitted. Sets --docs-font-heading.
typography.bodyFontstringpathFont stack for paragraph + inline content. Falls through to fontFamily when omitted. Sets --docs-font-body.
{
"typography": {
"headingFont": "'Geist', system-ui, sans-serif",
"bodyFont": "'Inter', system-ui, sans-serif"
}
}Complete example
Paste-ready block combining every appearance-related key. Drop into your nookdocs.config.json, tweak slugs/colors/paths, deploy.
{
"$schema": "https://nookdocs.com/schema.json",
"name": "Acme Docs",
"theme": "larch",
"appearance": {
"default": "system",
"strict": false
},
"colors": {
"primary": "#0ea5e9",
"light": "#0284c7",
"dark": "#38bdf8",
"logo": {
"light": "/logo/acme-light.svg",
"dark": "/logo/acme-dark.svg",
"href": "https://acme.com"
}
},
"background": {
"light": "#ffffff",
"dark": "radial-gradient(at top, #0f172a 0%, #020617 100%)"
},
"typography": {
"headingFont": "'Geist', system-ui, sans-serif",
"bodyFont": "'Inter', system-ui, sans-serif"
}
}Precedence rules
When multiple values could apply, NookDocs resolves in this order (last wins):
Theme default —
themes/<slug>.tsshipstokensLight+tokensDark(including the background)colors.primary— used for both modes when no per-mode overridecolors.light/colors.dark— overrides per-modebackground.*/typography.*— optional overrides, emitted as CSS custom properties at#docs-rootscope. A given mode is only overridden when itsbackgroundkey is present; otherwise the theme background stands.Preview header —
?preview_theme=<slug>(editor only) swaps the active theme definition at render time
This means colors.dark always wins over colors.primary in dark mode, and any per-tenant override always wins over the theme defaults — but an absent background key means "use the theme," not "use white/black."
Preview shows the theme, not your site. ?preview_theme= deliberately
skips your colors and background overrides so you can see each theme as it
was designed — otherwise every theme would render with the same overridden
background and they would look identical.
So if you have set background, preview and your live site will differ. That
is expected: preview answers "what does this theme look like," your live site
answers "what does my site look like." Remove the override (or use Reset to
theme in the dashboard) if you want your site to follow whichever theme you
pick.
Editing from the dashboard
Configurations → Site surfaces every field above. When you pick a theme, the accent and background fields preview that theme's own colors; they're only written to nookdocs.config.json if you edit them, and each field has a Reset to theme action to drop the override and fall back to the theme default. A field showing Theme default is following the theme and will keep following it through future theme switches — so you can try several themes and see each one's real palette, then pin only the colors you actually want to differ. The dashboard writes back to nookdocs.config.json in your repo on save (git commit via the GitHub App), so dashboard edits and hand-edits stay in sync. Run npm run check:sync before committing schema additions — see schema reference for the validator rules.
Related
Schema reference — auto-generated field-by-field reference for every
nookdocs.config.jsonkeyBrand — favicon, OG image, manifest, PWA assets beyond logos
Custom domain — DNS, SSL, and canonical URL setup
Site settings — banner, SEO, redirects, footer, navbar