Your First Theme
This tutorial shows you how to go from a single brand color to a complete, WCAG AA–validated theme in two commands.
How theme generation works
Section titled “How theme generation works”/theme-create takes one hex color — your “seed” — and derives a full 18-role semantic palette using OKLCH color math. Every light and dark value is tested against WCAG 2.2 AA contrast requirements before being written to disk.
The output is two plain CSS files:
src/styles/theme/light.css—:rootblock with all--color-*custom propertiessrc/styles/theme/dark.css—[data-theme="dark"]block with dark-mode values
Generate your theme
Section titled “Generate your theme”-
Run
/theme-createwith your brand’s primary hex color:/theme-create #4f46e5To generate both light and dark at once (the default):
/theme-create #4f46e5 --mode=both -
Claude runs the OKLCH palette algorithm and shows a validation summary:
Generating palette from #4f46e5 (oklch 55% 0.22 270°)...Light mode — contrast checks:✓ text / background 14.2 : 1 (AA ✓)✓ primary / background 5.8 : 1 (AA ✓)✓ text-inverse / primary 4.7 : 1 (AA ✓)✓ danger / background 5.1 : 1 (AA ✓)✓ success / background 4.6 : 1 (AA ✓)... (10 pairs total)Dark mode — contrast checks:✓ text / background 12.8 : 1 (AA ✓)... (10 pairs total)Writing src/styles/theme/light.css ✓Writing src/styles/theme/dark.css ✓ -
If any contrast pair fails, Claude adjusts the lightness value in OKLCH space and retries. The files are only written once all 10 pairs pass.
Inspect the output
Section titled “Inspect the output”src/styles/theme/light.css:
:root { /* Backgrounds */ --color-background: oklch(99% 0.004 270); --color-surface: oklch(97% 0.006 270); --color-surface-raised: oklch(95% 0.008 270);
/* Borders */ --color-border: oklch(88% 0.014 270); --color-border-strong: oklch(72% 0.018 270);
/* Text */ --color-text: oklch(16% 0.012 270); --color-text-inverse: oklch(99% 0.004 270); --color-text-muted: oklch(48% 0.014 270); --color-text-disabled: oklch(68% 0.010 270);
/* Brand */ --color-primary: oklch(55% 0.22 270); --color-primary-hover: oklch(48% 0.22 270); --color-focus-ring: oklch(63% 0.20 270);
/* Semantic */ --color-danger: oklch(50% 0.22 27); --color-warning: oklch(56% 0.18 80); --color-info: oklch(55% 0.18 240); --color-success: oklch(48% 0.18 155);}src/styles/theme/dark.css:
[data-theme="dark"] { --color-background: oklch(12% 0.008 270); --color-surface: oklch(16% 0.010 270); --color-surface-raised: oklch(20% 0.012 270); --color-primary: oklch(72% 0.18 270); /* ... all 18 roles ... */}Import the theme in your app
Section titled “Import the theme in your app”Add both theme files to your app’s entry point, in order:
import './styles/theme/light.css';import './styles/theme/dark.css';import './styles/utilities.css'; // if using acss-utilitiesimport './App.css';import App from './App';Enable dark mode
Section titled “Enable dark mode”Dark mode activates when data-theme="dark" is set on the <html> element:
// Simple dark mode togglefunction toggleTheme() { const html = document.documentElement; html.dataset.theme = html.dataset.theme === 'dark' ? '' : 'dark';}Create a brand override
Section titled “Create a brand override”Once you have a base theme, layer a brand file on top for named overrides:
/theme-brand forest --from=#2d6a4fThis generates src/styles/theme/brand-forest.css which overrides selected roles while inheriting the rest from light.css / dark.css.
→ See /theme-brand for the full reference.
Next: add utility classes
Section titled “Next: add utility classes”With your theme in place, the utility classes from acss-utilities can reference your --color-* tokens automatically.
→ Continue to Your First Utilities