Custom Themes
@theme-kit/core
Theme Kit ships with neutral, preset, brand and accessibility themes — but the real power is defining your own. Every theme is a plain object of semantic tokens plus metadata.
1Define a theme
The smallest building block. A name, an optional family + mode, and nested tokens that become CSS variables.
1import { defineTheme } from "@theme-kit/core";
2
3export const oceanLight = defineTheme({
4 name: "ocean-light",
5 meta: { family: "ocean", mode: "light", label: "Ocean Light" },
6 tokens: {
7 colors: {
8 background: "#f0f9ff",
9 foreground: "#0c1e2e",
10 primary: "#0284c7",
11 primaryForeground: "#ffffff",
12 secondary: "#e0f2fe",
13 secondaryForeground: "#0369a1",
14 muted: "#bae6fd",
15 mutedForeground: "#075985",
16 accent: "#0ea5e9",
17 accentForeground: "#ffffff",
18 destructive: "#dc2626",
19 destructiveForeground: "#ffffff",
20 success: "#16a34a",
21 successForeground: "#ffffff",
22 border: "#7dd3fc",
23 ring: "#0284c7",
24 },
25 radius: { lg: "0.5rem" },
26 },
27});Mode + family |Give light and dark variants the same
family so switching mode stays within your palette. See Core Concepts for how families work.1export const oceanDark = defineTheme({
2 name: "ocean-dark",
3 meta: { family: "ocean", mode: "dark", label: "Ocean Dark" },
4 tokens: {
5 colors: {
6 background: "#0c1e2e",
7 foreground: "#f0f9ff",
8 primary: "#38bdf8",
9 primaryForeground: "#0c1e2e",
10 secondary: "#0369a1",
11 secondaryForeground: "#e0f2fe",
12 muted: "#075985",
13 mutedForeground: "#bae6fd",
14 accent: "#0ea5e9",
15 accentForeground: "#0c1e2e",
16 destructive: "#f87171",
17 destructiveForeground: "#0c1e2e",
18 success: "#4ade80",
19 successForeground: "#0c1e2e",
20 border: "#0369a1",
21 ring: "#38bdf8",
22 },
23 radius: { lg: "0.5rem" },
24 },
25});2Extend & compose
Inherit from a base theme and override only the tokens that change.
1import { defineTheme, extendTheme } from "@theme-kit/core";
2import { oceanLight } from "./themes";
3
4const base = defineTheme({
5 name: "base",
6 meta: { family: "acme", mode: "light", label: "Acme Base" },
7 tokens: {
8 colors: {
9 background: "#fafafa",
10 foreground: "#18181b",
11 primary: "#6366f1",
12 primaryForeground: "#ffffff",
13 secondary: "#f4f4f5",
14 secondaryForeground: "#18181b",
15 muted: "#f4f4f5",
16 mutedForeground: "#71717a",
17 accent: "#f4f4f5",
18 accentForeground: "#18181b",
19 destructive: "#dc2626",
20 destructiveForeground: "#ffffff",
21 success: "#16a34a",
22 successForeground: "#ffffff",
23 border: "#e4e4e7",
24 ring: "#6366f1",
25 },
26 radius: { lg: "10px" },
27 },
28});
29
30export const acmeLight = extendTheme("acme-light", base, {
31 meta: { family: "acme", mode: "light", label: "Acme Light" },
32 colors: {
33 background: "#fafafa",
34 primary: "#4f46e5",
35 },
36});
37
38export const acmeDark = extendTheme("acme-dark", base, {
39 meta: { family: "acme", mode: "dark", label: "Acme Dark" },
40 colors: {
41 background: "#09090b",
42 foreground: "#fafafa",
43 primary: "#818cf8",
44 ring: "#818cf8",
45 },
46});3Register with the runtime
Pass your themes to the runtime or provider in any framework. The same list works everywhere.
1import { createThemeRuntime } from "@theme-kit/core";
2import { ThemeProvider } from "@theme-kit/react";
3import { oceanLight, oceanDark } from "./themes";
4
5const themes = [oceanLight, oceanDark];
6
7createThemeRuntime({
8 themes,
9 defaultTheme: "ocean-light",
10 initialMode: "system",
11});
12
13export function App() {
14 return (
15 <ThemeProvider themes={themes} defaultTheme="ocean-light">
16 <YourApp />
17 </ThemeProvider>
18 );
19}1import { ThemeKit } from "@theme-kit/core/vanilla";
2import { oceanLight, oceanDark } from "./themes";
3
4const kit = new ThemeKit({ themes: [oceanLight, oceanDark] });
5kit.setFamily("ocean");
6kit.setMode("light");
7kit.update({ colors: { primary: "#06b6d4" } });
8kit.destroy();4Generate from a seed
No token math needed — derive an entire light/dark pair from one color, then validate it.
1import { generateTheme } from "@theme-kit/core";
2
3const { light, dark } = generateTheme({
4 seed: "#6366f1",
5 family: "indigo",
6});
7
8import { validateTheme } from "@theme-kit/core";
9const result = validateTheme(dark, { themes: [light, dark] });
105Presets & brand themes
Compose on top of the built-in palettes instead of starting from scratch.
Default presets
Nine curated families — oat, berry, mint, citrus, cocoa, plum, iris, sky, graphite — each with light + dark. Grab them via getPresetThemes() or restyle with getPresetThemes(overrides).
1import { getPresetThemes } from "@theme-kit/core";
2
3const themes = getPresetThemes();
4
5const themes = getPresetThemes({
6 plum: {
7 light: { tokens: { colors: { primary: "#6d28d9" } } },
8 },
9});Brand presets
Real-world brand palettes — Apple, GitHub, Vercel, Slack, Discord — via getBrandPresets(). Each is a ready-to-register theme pair.
1import { getBrandPresets } from "@theme-kit/core";
2
3const themes = getBrandPresets();
4
5const brand = themes.filter((t) => t.meta?.family === "github");
6createThemeRuntime({ themes: brand, defaultTheme: "github-light" });Accessibility profiles
High-contrast and large-text variants ship via getAccessibilityProfiles() and are tagged "accessibility" for filtering.
6Use your custom theme
The theme you defined above is a plain object — the same two objects register in every framework. Pick yours to see the exact integration with the custom ocean family.
Pick your framework
1import { ThemeProvider, useTheme } from "@theme-kit/react";
2import { oceanLight, oceanDark } from "./themes";
3
4const themes = [oceanLight, oceanDark];
5
6export function App() {
7 return (
8 <ThemeProvider themes={themes} defaultTheme="ocean-light">
9 <ThemeSwitcher />
10 </ThemeProvider>
11 );
12}
13
14function ThemeSwitcher() {
15 const { theme, setMode, setFamily } = useTheme();
16 return (
17 <div>
18 <span>{theme.name}</span>
19 <button onClick={() => setFamily("ocean")}>ocean</button>
20 <button onClick={() => setMode("light")}>Light</button>
21 <button onClick={() => setMode("dark")}>Dark</button>
22 </div>
23 );
24}The
oceanLight and oceanDark themes come from section 1. Register both, set ocean-light as the default, and mode toggling stays inside your family. See Framework Guides for a deeper walkthrough of each integration.7What's next
Now that you have a custom theme, make it a first-class part of your product.