Color System Generator
Turn one brand color into a full set of color tokens: scales, accents, light and dark themes, ready to paste.
Part of design tools.
Used as --brand-primary-500.
- • Accent 1 had its chroma lowered to stay inside sRGB — same hue and lightness, a little less colorful.
Light
Ship your tokens
Body copy on a raised surface, measured against this theme.
Dark
Ship your tokens
Body copy on a raised surface, measured against this theme.
| Pair | Light | Dark |
|---|---|---|
| text / background | 17.70:1AAA | 16.06:1AAA |
| text / surface | 16.26:1AAA | 15.08:1AAA |
| text / surface-raised | 14.88:1AAA | 14.03:1AAA |
| text-muted / background | 7.75:1AAA | 14.93:1AAA |
| text-muted / surface | 7.12:1AAA | 14.02:1AAA |
| text-muted / surface-raised | 6.51:1AA | 13.04:1AAA |
| primary / background | 5.84:1AA | 4.62:1AA |
| on-primary / primary | 5.84:1AA | 2.67:1Fail |
| accent-1 / background | 5.23:1AA | 4.61:1AA |
| on-accent-1 / accent-1 | 5.23:1AA | 2.68:1Fail |
:root {
--brand-primary-50: #f4f4ff;
--brand-primary-100: #e9e9ff;
--brand-primary-200: #d3d2ff;
--brand-primary-300: #b8b4ff;
--brand-primary-400: #9b90ff;
--brand-primary-500: #8167ff;
--brand-primary-600: #6946e4;
--brand-primary-700: #522abe;
--brand-primary-800: #3c0c97;
--brand-primary-900: #28006d;
--brand-primary-950: #19004c;
--brand-accent-1-50: #f8f7dc;
--brand-accent-1-100: #efeec5;
--brand-accent-1-200: #dddc9e;
--brand-accent-1-300: #c6c270;
--brand-accent-1-400: #aba642;
--brand-accent-1-500: #908a11;
--brand-accent-1-600: #746f00;
--brand-accent-1-700: #595500;
--brand-accent-1-800: #403d00;
--brand-accent-1-900: #2a2800;
--brand-accent-1-950: #1b1900;
--brand-neutral-50: #f5f5f7;
--brand-neutral-100: #ebebee;
--brand-neutral-200: #d7d7dc;
--brand-neutral-300: #bdbdc4;
--brand-neutral-400: #a1a1a8;
--brand-neutral-500: #85858d;
--brand-neutral-600: #6b6b72;
--brand-neutral-700: #525259;
--brand-neutral-800: #3a3a40;
--brand-neutral-900: #26262b;
--brand-neutral-950: #18181c;
--brand-background: #ffffff;
--brand-surface: #f5f5f7;
--brand-surface-raised: #ebebee;
--brand-border: #d7d7dc;
--brand-text: #18181c;
--brand-text-muted: #525259;
--brand-primary: #6946e4;
--brand-on-primary: #ffffff;
--brand-accent-1: #746f00;
--brand-on-accent-1: #ffffff;
}
@media (prefers-color-scheme: dark) {
:root {
--brand-background: #0d0d0d;
--brand-surface: #151516;
--brand-surface-raised: #1d1c1e;
--brand-border: #2d2d30;
--brand-text: #e9e9ee;
--brand-text-muted: #e1e1e8;
--brand-primary: #7b69e5;
--brand-on-primary: #cecece;
--brand-accent-1: #817e39;
--brand-on-accent-1: #cecece;
}
}From one color to a whole theme
A design system rarely starts with a spreadsheet of hex codes — it starts with one brand color someone signed off on. This tool takes that color and derives everything the rest of the theme needs: an eleven-step primary scale, a neutral ramp tinted with the same hue, one or two accents rotated in OKLCH, and the semantic tokens (background, surface, border, text, muted text, accents and their labels) in both a light and a dark version.
Accents are rotations, not guesses
The accents come from the same harmony maths as the color harmonies tool: hue angles measured in OKLCH, where a 120° rotation keeps the perceived lightness and the colorfulness of the base instead of landing on something far darker, as the same rotation in HSL would. Pick complementary for a single loud accent, analogous for a calm pair, triadic or split-complementary in between. A grey brand color gets no accent at all: there is no hue to rotate, and inventing one out of rounding noise would be a bug rather than a feature.
The dark theme is derived, not inverted
The dark tokens use the same OKLCH conversion as the dark mode palette converter: surfaces are reflected into a narrow dark band, text lands high but never at pure white, and accents are lightened and desaturated until they clear the contrast target on the page background. When a target cannot be reached inside sRGB, you get the closest color plus a warning — never a token that quietly fails.
Read the verdicts before you ship
Every text/surface pair the theme actually uses is measured in both themes, with its ratio and its AA/AAA badge for normal text. That includes each accent against the page and each accent label against its own accent, which is the pair most systems get wrong. For a one-off check of a pair the table does not cover, the contrast checker gives the full verdict, and the palette generator is the single-scale version of this page.
Three ways to paste it
The CSS export declares the scales and the light tokens in :root and re-declares only the semantic tokens inside a @media (prefers-color-scheme: dark) block — the scales themselves do not change with the theme, only which step each role points at. The Tailwind export is a theme.extend.colors object, and the JSON one is the same data for whatever pipeline you feed. Everything runs in your browser; nothing is uploaded.