Colors
The intent-to-value mapping for the seven Gremorie themes, in light and dark mode.
Semantic tokens are intents: primary, foreground,
border. Each theme fills those intents with different values,
and each mode (light / dark) brings its own mapping. This page is
the truth table for the seven bundled themes: Default, Claude,
ChatGPT, Gemini, Perplexity, Mistral and Purple.
How to read the tables
- The Light and Dark columns render the exact declared value as a chip over that theme's own light or dark background, so both are faithful no matter which mode you are reading in.
- The Value columns show what the theme declares: a primitive
name (an alias in the DTCG source, written to CSS as
var(--color-<token>)) or, where the theme matches a product surface that has no primitive equivalent, a literal OKLCH value. - A
basebadge means the theme does not override that token; it inherits the value from the base layer (semantic/light.jsonandsemantic/dark.json). That inheritance is the shipped behavior, not an omission.
If you are auditing a component that looks "wrong" in some theme,
start here. Comparing the intent (e.g. --primary) with the value
it resolves to in each theme tells you whether the inconsistency
comes from the component chrome or from the theme mapping.
Default
The base semantic layer itself, on the neutralized gray ramp. Conservative and brand-free; every other theme is an override layer on top of it.
--radius-base: 0.625rem · selector :root
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | gray-50base | gray-950base | ||
| --foreground | gray-950base | gray-50base | ||
| --card | gray-0base | gray-900base | ||
| --card-foreground | gray-950base | gray-50base | ||
| --popover | gray-0base | gray-900base | ||
| --popover-foreground | gray-950base | gray-50base | ||
| --primary | gray-900base | gray-100base | ||
| --primary-foreground | gray-50base | gray-900base | ||
| --secondary | gray-100base | gray-800base | ||
| --secondary-foreground | gray-900base | gray-50base | ||
| --muted | gray-100base | gray-800base | ||
| --muted-foreground | gray-500base | gray-400base | ||
| --accent | gray-100base | gray-800base | ||
| --accent-foreground | gray-900base | gray-50base | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-50base | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --input | gray-200base | oklch(1 0 0 / 15%)base | ||
| --ring | gray-400base | gray-500base | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | gray-50base | gray-900base | ||
| --sidebar-foreground | gray-950base | gray-50base | ||
| --sidebar-primary | gray-900base | gray-100base | ||
| --sidebar-primary-foreground | gray-50base | gray-900base | ||
| --sidebar-accent | gray-100base | gray-800base | ||
| --sidebar-accent-foreground | gray-900base | gray-50base | ||
| --sidebar-border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --sidebar-ring | gray-400base | gray-500base |
Claude
Warm clay (coral terracotta) on cream surfaces, taupe support, serif display. Dark mode sits on the warm charcoal of the real product.
--radius-base: 0.5rem · selector [data-theme='claude']
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | oklch(0.965 0.011 92) | oklch(0.235 0.006 100) | ||
| --foreground | gray-950 | gray-50 | ||
| --card | oklch(0.985 0.008 92) | oklch(0.268 0.005 100) | ||
| --card-foreground | gray-950 | gray-50 | ||
| --popover | oklch(0.985 0.008 92) | oklch(0.268 0.005 100) | ||
| --popover-foreground | gray-950 | gray-50 | ||
| --primary | clay-500 | clay-400 | ||
| --primary-foreground | gray-50 | gray-950 | ||
| --secondary | oklch(0.95 0.012 80) | oklch(0.28 0.012 58) | ||
| --secondary-foreground | clay-900 | gray-50 | ||
| --muted | oklch(0.95 0.012 80) | oklch(0.28 0.012 58) | ||
| --muted-foreground | taupe-600 | taupe-300 | ||
| --accent | clay-100 | clay-900 | ||
| --accent-foreground | clay-900 | clay-50 | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-50base | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | oklch(0.9 0.014 78) | oklch(1 0 0 / 10%) | ||
| --input | oklch(0.9 0.014 78) | oklch(1 0 0 / 15%) | ||
| --ring | clay-500 | clay-400 | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | oklch(0.97 0.01 84) | oklch(0.24 0.012 60) | ||
| --sidebar-foreground | gray-950 | gray-50 | ||
| --sidebar-primary | clay-600 | clay-400 | ||
| --sidebar-primary-foreground | gray-50 | gray-950 | ||
| --sidebar-accent | clay-100 | oklch(0.28 0.012 58) | ||
| --sidebar-accent-foreground | clay-900 | gray-50 | ||
| --sidebar-border | oklch(0.9 0.014 78) | oklch(1 0 0 / 10%) | ||
| --sidebar-ring | clay-500 | clay-400 |
ChatGPT
Emerald accent on a flat, low-shadow surface. Dark mode uses the flat graphite of the real product; surfaces otherwise inherit the neutral base.
--radius-base: 0.75rem · selector [data-theme='chatgpt']
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | gray-50base | oklch(0.248 0 0) | ||
| --foreground | gray-950base | gray-50base | ||
| --card | gray-0base | oklch(0.278 0 0) | ||
| --card-foreground | gray-950base | gray-50base | ||
| --popover | gray-0base | oklch(0.278 0 0) | ||
| --popover-foreground | gray-950base | gray-50base | ||
| --primary | emerald-700 | emerald-400 | ||
| --primary-foreground | gray-50 | gray-950 | ||
| --secondary | gray-100base | gray-800base | ||
| --secondary-foreground | gray-900base | gray-50base | ||
| --muted | gray-100base | gray-800base | ||
| --muted-foreground | gray-500base | gray-400base | ||
| --accent | emerald-100 | emerald-900 | ||
| --accent-foreground | emerald-900 | emerald-50 | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-50base | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --input | gray-200base | oklch(1 0 0 / 15%)base | ||
| --ring | emerald-500 | emerald-400 | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | gray-50base | gray-900base | ||
| --sidebar-foreground | gray-950base | gray-50base | ||
| --sidebar-primary | emerald-700 | emerald-400 | ||
| --sidebar-primary-foreground | gray-50 | gray-950 | ||
| --sidebar-accent | gray-100base | gray-800base | ||
| --sidebar-accent-foreground | gray-900base | gray-50base | ||
| --sidebar-border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --sidebar-ring | emerald-500 | emerald-400 |
Gemini
Google blue with a blue-to-purple brand ramp and the roundest radius of the set. Dark mode sits on the cool graphite of the real product.
--radius-base: 1rem · selector [data-theme='gemini']
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | gray-50base | oklch(0.239 0.002 248) | ||
| --foreground | gray-950base | gray-50base | ||
| --card | gray-0base | oklch(0.274 0.002 248) | ||
| --card-foreground | gray-950base | gray-50base | ||
| --popover | gray-0base | oklch(0.274 0.002 248) | ||
| --popover-foreground | gray-950base | gray-50base | ||
| --primary | oklch(0.55 0.19 258) | oklch(0.75 0.11 258) | ||
| --primary-foreground | gray-50 | gray-950 | ||
| --secondary | gray-100base | gray-800base | ||
| --secondary-foreground | gray-900base | gray-50base | ||
| --muted | gray-100base | gray-800base | ||
| --muted-foreground | gray-500base | gray-400base | ||
| --accent | indigo-100 | indigo-900 | ||
| --accent-foreground | indigo-900 | indigo-50 | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-50base | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --input | gray-200base | oklch(1 0 0 / 15%)base | ||
| --ring | oklch(0.63 0.18 260) | oklch(0.75 0.11 258) | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | gray-50base | gray-900base | ||
| --sidebar-foreground | gray-950base | gray-50base | ||
| --sidebar-primary | oklch(0.55 0.19 258) | oklch(0.75 0.11 258) | ||
| --sidebar-primary-foreground | gray-50 | gray-950 | ||
| --sidebar-accent | gray-100base | gray-800base | ||
| --sidebar-accent-foreground | gray-900base | gray-50base | ||
| --sidebar-border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --sidebar-ring | oklch(0.63 0.18 260) | oklch(0.75 0.11 258) |
Perplexity
True turquoise on offblack, sober and tight, with the smallest radius of the set. The light primary is the brand turquoise #20808D.
--radius-base: 0.375rem · selector [data-theme='perplexity']
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | gray-50base | oklch(0.192 0.02 195.6) | ||
| --foreground | gray-950base | gray-50base | ||
| --card | gray-0base | oklch(0.225 0.018 196) | ||
| --card-foreground | gray-950base | gray-50base | ||
| --popover | gray-0base | oklch(0.225 0.018 196) | ||
| --popover-foreground | gray-950base | gray-50base | ||
| --primary | oklch(0.553 0.086 208.5) | oklch(0.72 0.11 205) | ||
| --primary-foreground | gray-0 | gray-950 | ||
| --secondary | gray-100base | gray-800base | ||
| --secondary-foreground | gray-900base | gray-50base | ||
| --muted | gray-100base | gray-800base | ||
| --muted-foreground | gray-500base | gray-400base | ||
| --accent | teal-100 | teal-900 | ||
| --accent-foreground | teal-900 | teal-50 | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-50base | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --input | gray-200base | oklch(1 0 0 / 15%)base | ||
| --ring | oklch(0.553 0.086 208.5) | oklch(0.72 0.11 205) | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | gray-50base | gray-900base | ||
| --sidebar-foreground | gray-950base | gray-50base | ||
| --sidebar-primary | oklch(0.553 0.086 208.5) | oklch(0.72 0.11 205) | ||
| --sidebar-primary-foreground | gray-0 | gray-950 | ||
| --sidebar-accent | gray-100base | gray-800base | ||
| --sidebar-accent-foreground | gray-900base | gray-50base | ||
| --sidebar-border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --sidebar-ring | oklch(0.553 0.086 208.5) | oklch(0.72 0.11 205) |
Mistral
Flame orange with a black-on-orange primary, matching Mistral brand usage. Surfaces inherit the neutral base in both modes.
--radius-base: 0.5rem · selector [data-theme='mistral']
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | gray-50base | gray-950base | ||
| --foreground | gray-950base | gray-50base | ||
| --card | gray-0base | gray-900base | ||
| --card-foreground | gray-950base | gray-50base | ||
| --popover | gray-0base | gray-900base | ||
| --popover-foreground | gray-950base | gray-50base | ||
| --primary | orange-600 | orange-400 | ||
| --primary-foreground | gray-950 | gray-950 | ||
| --secondary | gray-100base | gray-800base | ||
| --secondary-foreground | gray-900base | gray-50base | ||
| --muted | gray-100base | gray-800base | ||
| --muted-foreground | gray-500base | gray-400base | ||
| --accent | orange-100 | orange-900 | ||
| --accent-foreground | orange-900 | orange-50 | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-50base | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --input | gray-200base | oklch(1 0 0 / 15%)base | ||
| --ring | orange-500 | orange-400 | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | gray-50base | gray-900base | ||
| --sidebar-foreground | gray-950base | gray-50base | ||
| --sidebar-primary | orange-600 | orange-400 | ||
| --sidebar-primary-foreground | gray-950 | gray-950 | ||
| --sidebar-accent | gray-100base | gray-800base | ||
| --sidebar-accent-foreground | gray-900base | gray-50base | ||
| --sidebar-border | gray-200base | oklch(1 0 0 / 10%)base | ||
| --sidebar-ring | orange-500 | orange-400 |
Purple
Violet on lavender surfaces, adapted from the Fumadocs Purple preset (MIT). Two pairs are corrected for WCAG AA: a near-white primary foreground in light mode and a near-black destructive foreground in dark mode.
--radius-base: 0.625rem · selector [data-theme='purple']
| Light | Dark | Token | Value (light) | Value (dark) |
|---|---|---|---|---|
| --background | oklch(0.9491 0.0278 298.05) | oklch(0.1605 0.013 297.13) | ||
| --foreground | oklch(0.3035 0.1296 288.37) | oklch(0.8926 0.0424 297.73) | ||
| --card | oklch(0.9142 0.0337 297.92) | oklch(0.1992 0.0236 296.44) | ||
| --card-foreground | oklch(0.2481 0.1513 278.88) | oklch(0.8926 0.0424 297.73) | ||
| --popover | oklch(0.9572 0.0166 298.26) | oklch(0.1592 0.015 296.89) | ||
| --popover-foreground | oklch(0.2481 0.1513 278.88) | oklch(0.8926 0.0424 297.73) | ||
| --primary | oklch(0.5379 0.2912 295.26) | oklch(0.8366 0.1046 307.06) | ||
| --primary-foreground | oklch(0.9764 0.0145 308.3) | oklch(0.1434 0.0399 294.09) | ||
| --secondary | oklch(0.8983 0.0444 307.91) | oklch(0.2979 0.0443 306.78) | ||
| --secondary-foreground | oklch(0.1783 0.0597 291.44) | oklch(0.8926 0.0424 297.73) | ||
| --muted | oklch(0.9357 0.0251 298.1) | oklch(0.1992 0.0236 296.44) | ||
| --muted-foreground | oklch(0.4838 0.1888 289.71) | oklch(0.7413 0.0922 296.32) | ||
| --accent | oklch(0.8876 0.0356 308.02) | oklch(0.3093 0.0463 295.83) | ||
| --accent-foreground | oklch(0.2757 0.1499 296.78) | oklch(0.8687 0.0418 307.93) | ||
| --destructive | red-600base | red-400base | ||
| --destructive-foreground | gray-50base | gray-950 | ||
| --warning | amber-500base | amber-400base | ||
| --warning-foreground | gray-950base | gray-950base | ||
| --success | green-700base | green-400base | ||
| --success-foreground | gray-50base | gray-950base | ||
| --border | oklch(0.8116 0.0606 307.63 / 0.5) | oklch(0.4403 0.1603 303.37 / 0.3) | ||
| --input | oklch(0.8116 0.0606 307.63 / 0.5) | oklch(0.4403 0.1603 303.37 / 0.3) | ||
| --ring | oklch(0.5379 0.2912 295.26) | oklch(0.8366 0.1046 307.06) | ||
| --chart-1 | blue-500base | blue-600base | ||
| --chart-2 | amber-500base | amber-600base | ||
| --chart-3 | green-500base | green-600base | ||
| --chart-4 | cyan-500base | cyan-600base | ||
| --chart-5 | pink-500base | pink-600base | ||
| --sidebar | oklch(0.9491 0.0278 298.05) | oklch(0.1605 0.013 297.13) | ||
| --sidebar-foreground | oklch(0.3035 0.1296 288.37) | oklch(0.8926 0.0424 297.73) | ||
| --sidebar-primary | oklch(0.5379 0.2912 295.26) | oklch(0.8366 0.1046 307.06) | ||
| --sidebar-primary-foreground | oklch(0.9764 0.0145 308.3) | oklch(0.1434 0.0399 294.09) | ||
| --sidebar-accent | oklch(0.8876 0.0356 308.02) | oklch(0.3093 0.0463 295.83) | ||
| --sidebar-accent-foreground | oklch(0.2757 0.1499 296.78) | oklch(0.8687 0.0418 307.93) | ||
| --sidebar-border | oklch(0.8116 0.0606 307.63 / 0.5) | oklch(0.4403 0.1603 303.37 / 0.3) | ||
| --sidebar-ring | oklch(0.5379 0.2912 295.26) | oklch(0.8366 0.1046 307.06) |
About the sidebar tokens
The --sidebar-* tokens are a parallel family covering the
side navigation chrome. They exist because sidebars are surfaces
often painted differently from background (darker, lighter,
contrasting). Keeping dedicated intents avoids the "tinted
background" trick that breaks when the theme changes.
About the chart tokens
--chart-1 through --chart-5 alias the categorical scale
(--chart-cat-1..5), which is not themeable: every theme shows the
same series colors. See Chart tokens.
Editing or adding themes
Themes are override layers on top of the base semantic layer. Their source
is DTCG JSON in packages/tokens/tokens/themes/<id>.json; the CSS in
packages/tokens/styles/ is generated from it. The short recipe:
- Create
packages/tokens/tokens/themes/<id>.jsonwith alightand adarkgroup, and add<id>toTHEMESinpackages/tokens/pipeline/model.ts. - Override only what differs from the base, as DTCG tokens: an alias names
the token path (
{color.emerald.700}); a literal is an OKLCH color object. Whatever the light group declares as a literal color, the dark group must declare again, because:root[data-theme]outranks.dark. - Build with
nx build tokens. The theme gets its CSS file, its@importintheme.css, and its JSON, TypeScript, Kotlin and Swift outputs. - Audit with
nx test tokens: the contrast test fails on any text pair under 4.5:1 and any focus ring under 3:1, unless the pair is listed incontrast-deviations.jsonwith a reason.
Reach for primitives first. Reference the ramps by alias
({color.<ramp>.<step>}) so a primitive edit cascades into every theme. The
shipped themes hardcode OKLCH only where they match a product surface that
has no primitive equivalent (e.g. the Claude cream background or the Gemini
graphite). If your value is reusable, add it as a primitive instead.