/theme-create
Generate a full 18-role semantic color palette in OKLCH color space from a single hex color. Every role is WCAG 2.2 AA contrast-validated before the files are written.
Syntax
Section titled “Syntax”/theme-create <hex-color> [--mode=light|dark|both]Arguments
Section titled “Arguments”| Argument | Required | Default | Description |
|---|---|---|---|
hex-color | Yes | — | Seed color as a 6-digit hex (e.g., #4f46e5) |
--mode | No | both | Which theme files to generate: light, dark, or both |
Examples
Section titled “Examples”Generate both light and dark themes from indigo:
/theme-create #4f46e5Generate only the light theme:
/theme-create #4f46e5 --mode=lightGenerate only the dark theme:
/theme-create #2563eb --mode=darkOutput files
Section titled “Output files”| File | Contents |
|---|---|
src/styles/theme/light.css | :root block with all 18 --color-* custom properties |
src/styles/theme/dark.css | [data-theme="dark"] block with dark-mode values |
The 18 semantic color roles
Section titled “The 18 semantic color roles”Required (15)
Section titled “Required (15)”| Role | Purpose |
|---|---|
--color-background | Page background |
--color-surface | Card/panel background |
--color-surface-raised | Elevated surface (dropdowns, modals) |
--color-border | Default border |
--color-border-strong | Emphasized border |
--color-text | Body text |
--color-text-inverse | Text on colored backgrounds |
--color-text-muted | Secondary/caption text |
--color-text-disabled | Disabled state text |
--color-primary | Primary brand action |
--color-primary-hover | Primary hover state |
--color-focus-ring | Keyboard focus outline |
--color-danger | Destructive / error state |
--color-warning | Warning state |
--color-info | Informational state |
--color-success | Success / positive state |
Optional (3, generated when hue allows)
Section titled “Optional (3, generated when hue allows)”| Role | Purpose |
|---|---|
--color-accent | Secondary brand accent |
--color-accent-hover | Accent hover state |
--color-brand-accent | Decorative brand color |
OKLCH palette algorithm
Section titled “OKLCH palette algorithm”The palette generation uses OKLCH color space for perceptually uniform scaling:
- Parse seed → convert
#hexto OKLCH(L%, C, H°) - Set primary → use seed hue as
--color-primary; find highest lightness where contrast ≥ 4.5:1 against background - Derive backgrounds → high lightness (L 97–99%) for light mode, low lightness (L 12–20%) for dark
- Derive text → lightness that achieves ≥ 4.5:1 against its paired background
- Semantic hues → danger at H°27, warning at H°80, success at H°155, info at H°240 — derived chroma from seed
- Hover states →
primary-hover= primary with L reduced by ~5 steps;focus-ring= primary with L raised by ~8 steps - WCAG validation → check all 10 contrast pairs; adjust lightness if any fail; retry up to 8 times
WCAG contrast pairs validated
Section titled “WCAG contrast pairs validated”| Foreground | Background | Minimum |
|---|---|---|
--color-text | --color-background | 4.5:1 |
--color-primary | --color-background | 4.5:1 |
--color-text-inverse | --color-primary | 4.5:1 |
--color-danger | --color-background | 4.5:1 |
--color-success | --color-background | 4.5:1 |
--color-warning | --color-background | 3.0:1 |
--color-info | --color-background | 4.5:1 |
--color-text-muted | --color-background | 4.5:1 |
--color-border | --color-background | 3.0:1 |
--color-text | --color-surface | 4.5:1 |
Failure handling
Section titled “Failure handling”If a contrast pair cannot pass AA after 8 lightness iterations, the command:
- Reports the failing pair and its computed ratio
- Suggests an alternative seed color with sufficient contrast range
- Does not write any files (atomic — either all pass or nothing is written)
Dark mode output format
Section titled “Dark mode output format”Dark mode values are written in [data-theme="dark"] selector blocks (not @media prefers-color-scheme). This gives you manual control over when dark mode activates:
[data-theme="dark"] { --color-background: oklch(12% 0.008 270); /* ... */}To activate dark mode, set data-theme="dark" on <html>:
document.documentElement.dataset.theme = 'dark';