Generate a theme from a seed
Pick a seed color and Themvora derives a complete light + dark pair — secondary, muted, accent, card, popover, input, border and ring colors are all computed in OKLCH by generateTheme(). Apply it to this very site to see the runtime re-theme everything live.
{
"light": {
"name": "indigo-light",
"meta": {
"family": "indigo",
"mode": "light",
"label": "Indigo Light",
"order": 10,
"generation": {
"kind": "generated",
"algorithm": "semantic-v2",
"seed": "#6366f1",
"contrast": {
"text": 4.5,
"largeText": 3,
"ui": 3
},
"style": "balanced"
}
},
"tokens": {
"colors": {
"background": "#f1f1f6",
"foreground": "#191a1f",
"muted": "#e7e7ec",
"mutedForeground": "#646569",
"card": "#fafbff",
"cardForeground": "#191a1f",
"popover": "#f5f6fb",
"popoverForeground": "#191a1f",
"primary": "#5d5fea",
"primaryForeground": "#fbfbff",
"secondary": "#e2e3ea",
"secondaryForeground": "#232428",
"accent": "#a563d4",
"accentForeground": "#101116",
"destructive": "#bb0916",
"destructiveForeground": "#fbfbff",
"success": "#00782d",
"successForeground": "#fbfbff",
"border": "#d8d9de",
"input": "#86878c",
"ring": "#6366f1"
},
"radius": {
"lg": "8px"
}
}
},
"dark": {
"name": "indigo-dark",
"meta": {
"family": "indigo",
"mode": "dark",
"label": "Indigo Dark",
"order": 20,
"generation": {
"kind": "generated",
"algorithm": "semantic-v2",
"seed": "#6366f1",
"contrast": {
"text": 4.5,
"largeText": 3,
"ui": 3
},
"style": "balanced"
}
},
"tokens": {
"colors": {
"background": "#0d0d10",
"foreground": "#edeef3",
"muted": "#232427",
"mutedForeground": "#a3a4a8",
"card": "#1a1a1d",
"cardForeground": "#edeef3",
"popover": "#1e1f22",
"popoverForeground": "#edeef3",
"primary": "#808aff",
"primaryForeground": "#101116",
"secondary": "#26272c",
"secondaryForeground": "#e7e7ec",
"accent": "#b37ddc",
"accentForeground": "#101116",
"destructive": "#d01d21",
"destructiveForeground": "#fbfbff",
"success": "#008332",
"successForeground": "#fbfbff",
"border": "#323337",
"input": "#696a6e",
"ring": "#808aff"
},
"radius": {
"lg": "8px"
}
}
}
}import { generateTheme } from "@themvora/core";
/**
* One seed, two themes. Every colour below is derived — secondary, muted,
* accent, card, popover, input, border and ring included. Nothing is authored,
* so nothing drifts out of contrast with anything else.
*/
export const { light: indigoLight, dark: indigoDark } = generateTheme({
seed: "#6366f1",
family: "indigo",
});
// Hand the pair straight to the provider:
export const themes = [indigoLight, indigoDark];
/* Light theme */
:root {
--theme-color-background: #f1f1f6;
--theme-color-foreground: #191a1f;
--theme-color-muted: #e7e7ec;
--theme-color-mutedForeground: #646569;
--theme-color-card: #fafbff;
--theme-color-cardForeground: #191a1f;
--theme-color-popover: #f5f6fb;
--theme-color-popoverForeground: #191a1f;
--theme-color-primary: #5d5fea;
--theme-color-primaryForeground: #fbfbff;
--theme-color-secondary: #e2e3ea;
--theme-color-secondaryForeground: #232428;
--theme-color-accent: #a563d4;
--theme-color-accentForeground: #101116;
--theme-color-destructive: #bb0916;
--theme-color-destructiveForeground: #fbfbff;
--theme-color-success: #00782d;
--theme-color-successForeground: #fbfbff;
--theme-color-border: #d8d9de;
--theme-color-input: #86878c;
--theme-color-ring: #6366f1;
--theme-radius-lg: 8px;
}
/* Dark theme */
.dark {
--theme-color-background: #0d0d10;
--theme-color-foreground: #edeef3;
--theme-color-muted: #232427;
--theme-color-mutedForeground: #a3a4a8;
--theme-color-card: #1a1a1d;
--theme-color-cardForeground: #edeef3;
--theme-color-popover: #1e1f22;
--theme-color-popoverForeground: #edeef3;
--theme-color-primary: #808aff;
--theme-color-primaryForeground: #101116;
--theme-color-secondary: #26272c;
--theme-color-secondaryForeground: #e7e7ec;
--theme-color-accent: #b37ddc;
--theme-color-accentForeground: #101116;
--theme-color-destructive: #d01d21;
--theme-color-destructiveForeground: #fbfbff;
--theme-color-success: #008332;
--theme-color-successForeground: #fbfbff;
--theme-color-border: #323337;
--theme-color-input: #696a6e;
--theme-color-ring: #808aff;
--theme-radius-lg: 8px;
}generateTheme() recipes
This page exercises one call. These are the rest of the options, each one run against the shipped function rather than described from its signature.
One required option. Family defaults to "generated", so the pair is named generated-light and generated-dark.
import { generateTheme } from "@themvora/core";
const { light, dark } = generateTheme({ seed: "#6366f1" });
light.name; // "generated-light"
light.meta.family; // "generated"The family is what the runtime selects on, so name it something your app can refer to.
const { light, dark } = generateTheme({
seed: "#6366f1",
family: "ocean",
});
light.name; // "ocean-light"
dark.name; // "ocean-dark"Six styles share one generator — they differ in how the ramp is shaped, not in which colours are derived. balanced is the default.
const { light, dark } = generateTheme({
seed: "#6366f1",
family: "ocean",
style: "editorial",
// "balanced" | "soft" | "vibrant" | "editorial" | "minimal" | "brand"
});Opt in to a code-token block: nineteen syntax roles derived from the same seed, so highlighted code belongs to the theme rather than fighting it.
const { light, dark } = generateTheme({
seed: "#6366f1",
family: "ocean",
withCode: true,
});
Object.keys(light.tokens.code).length; // 19Hex is the common case, but rgb() and hsl() are accepted too — useful when the seed comes from a picker or a design token. Named colours are not.
generateTheme({ seed: "#6366f1" });
generateTheme({ seed: "rgb(99 102 241)" });
generateTheme({ seed: "hsl(239 84% 67%)" });
// An unparseable seed throws rather than silently falling back:
generateTheme({ seed: "rebeccapurple" });
// ColorError: generateTheme() received an invalid seed "rebeccapurple".Both modes are solved from the same identity, but neither is a filter over the other — a dark theme is not its light theme inverted.
const { light, dark } = generateTheme({ seed: "#6366f1" });
light.tokens.colors.primary; // "#5d5fea"
dark.tokens.colors.primary; // "#808aff"
// Both clear the same contrast contract against their own canvas.