One of the 26 palette names. Matched case-insensitively; anything unrecognized
falls back to violet.
Theme Variants
lib/theme-variants.ts registers 26 named palettes. Each one carries a full set
of light values, a full set of dark values, and a remap of Tailwind's blue
scale, all emitted as CSS custom properties into an inline <style> in the
document head. Picking one is a single environment variable: no code, no CSS
overrides. What follows covers the names, the stop-to-role mapping, and
everything else that setting drives.
Quick Start
Leave the default in place and the site renders in violet.
THEME_VARIANT=violetThe server reads the variable once at startup, so restart npm run dev after
changing it.
Adding Options
Every name below is a valid value. The resolver matches names in lowercase, so
AMBER and amber both land on the same palette.
violet
Default. Own OKLCH scale at hue 296, the OwnDocs brand hue.
amber
Own OKLCH scale of 11 stops at hue 72.
blue
Own OKLCH scale at hue 250, an actual blue.
cyan
Own OKLCH scale.
emerald
Green-leaning OKLCH scale at hue 162, distinct from green and teal.
fuchsia
OKLCH scale shared with pink.
green
Own OKLCH scale at hue 148.
indigo
Own OKLCH scale.
lime
Own OKLCH scale.
mauve
Near-neutral scale with a faint magenta cast.
mist
Near-neutral scale with a faint blue cast.
neutral
Same near-neutral scale as zinc.
olive
Near-neutral scale with a faint yellow-green cast.
orange
Own OKLCH scale.
pink
Own OKLCH scale at hue 345.
purple
Own OKLCH scale at hue 308.
red
Own OKLCH scale at hue 25.
rose
Own OKLCH scale at hue 10.
sky
Own OKLCH scale, and an actual blue.
slate
Same near-neutral scale as zinc.
stone
Warm-leaning near-neutral scale.
taupe
Near-neutral scale with a faint warm brown cast.
teal
Own OKLCH scale.
yellow
Own OKLCH scale at hue 95.
zinc
Cool-leaning near-neutral scale.
gray
Same near-neutral scale as zinc.
THEME_VARIANT=slateFour names resolve to one identical near-neutral scale:
gray,neutral,slate, andzinc
Every other name has its own hue. That leaves 23 visually distinct results across the 26 names.
Advanced
Deploy platforms set the same variable, so a Render service picks its palette
from render.yaml instead of a local file. An unrecognized name falls back to
violet rather than breaking the build.
envVars:
- key: THEME_VARIANT
value: violetWhat the chosen variant produces is a single CSS string, formatted here for reading and trimmed to a few properties:
:root {
--color-bg-primary: #fcfefd;
--color-accent: oklch(53.5% 0.1425 250);
--owndocs-brand-500: oklch(62.8% 0.15 250);
}
.dark {
--color-bg-primary: oklch(18% 0.039 250);
--color-accent: oklch(73.8% 0.1166 250);
}The generated themes map palette stops to roles the same way every time: light
mode takes its surfaces from stops 50 and 100, borders from 200 and 300, and the
accent from 600 with 700 on hover. Dark mode takes surfaces from 950, 900, and
800, borders from 800 and 700, and the accent from 400 with 300 on hover. Every
theme, including emerald, goes through the same buildThemeFromPalette()
function. None of them are hand-written exceptions.
Two more things ride on the same setting:
- The dark-mode toggle is separate from the palette. Readers pick light or dark
themselves; the toggle stores that choice in
localStorageunderdocs-theme-modeand flips the.darkclass on<html>. The variant supplies the colors for both sides of that switch. - Open Graph images read the same variant.
app/og/route.tsxpulls--color-bg-primary,--color-text-primary,--color-text-tertiary,--color-accent, and--color-border-primarystraight off it, so social cards match the site.
Components never reference a palette directly. They use the semantic aliases:
surface-primary, surface-secondary, surface-tertiary, content-primary,
content-secondary, content-tertiary, edge-primary, edge-secondary, and
brand, each resolving to whichever variant is loaded.
Options
THEME_VARIANTenvDefault: violetAVAILABLE_THEMESstring[]The 26 registered names, exported from lib/theme-variants.ts for anything
that needs to list or validate them.
DEFAULT_THEMEstringDefault: violetThe fallback used when THEME_VARIANT is unset or unknown.
getThemeVariant(name?: string) => ThemeVariantResolves a name to its variant, lowercasing first and falling back to the default. Used by both the root layout and the Open Graph image route.
generateThemeCss(theme: ThemeVariant) => stringSerializes a variant into the :root { ... } .dark { ... } string injected
into the document head.
lightRecord<string, string>The 16 light-mode custom properties on a variant, including
--color-api-code-bg.
darkRecord<string, string>The dark-mode custom properties, matching light key for key.
blueRecord<string, string>Eleven --color-blue-* overrides that repoint Tailwind's blue scale at the
variant's own stops, so existing blue utility classes follow the theme.