Not just palettes: a tool to build flexible and robust semantic colour systems
Ok, I know, not another palette generator right? What's always frustrated me as a lazy designer is that most tools only give you half the story. Sure they'll give you seven hundred wonderfully complementary shades, but how are you supposed to use those in your product?
Generating a generic colour system is the easy part (it's just maths), the difficult bit is using these palettes to create a system that actually works in a UI: layered surfaces, accessible foreground pairings, semantic roles that make sense in both light and dark mode.
This tool generates a complete semantic colour system – using semantics battle-tested in large production apps – from two inputs: a primary colour and a neutral. From there, it derives complementary scales for success, destructive and warning states, maps everything to semantic tokens with adaptive dark mode, and checks every pairing for contrast compliance.
And then let's you export to code in a whole bunch of different formats... even Lovable for you Vibers.
Generating tokens…
Semantic tokens, not just shades
Each primitive colour gets three semantic variants: a default for interactive elements, a subdued tint for backgrounds, and a highlight for emphasis. Each variant is paired with an automatically computed foreground colour that passes your chosen contrast standard.
The background shades for each variant uses a sensible default from the 12-shade scale, then the tool walks through the primitive scale using your chosen contrast algorithm (WCAG or APCA) and selects the first shade that meets the contrast and accessibility threshold.
You can override any of these mappings per-colour, per-mode, directly in the tool.
Default
Interactive elements — buttons, links, selections.
success surface with success-foreground text.
Subdued
Notices, badges, tinted surfaces
Changes saved
Your profile has been updated.
success-subdued surface with success-subdued-foreground text
Surface Layers
Three depth levels for layering UI — page background, cards, and popovers.
canvas
surface
overlay
canvas is the page background. surface sits on top (cards, sections). overlay is the highest layer (dropdowns, dialogs).
Utility
Borders, focus rings, inputs, and shadows.
border for dividers and edges. input for form field borders. ring for focus indicators. shadow for elevation.
Derived semantic scales
Rather than asking you to manually pick a red, green and amber that happen to work with your brand, the tool derives them from your primary colour. It reads the primary's chroma and lightness in OKLCH, then generates success (hue 145), destructive (hue 25) and warning (hue 75) colours whose energy and saturation complement the accent you chose.
The result is a set of status colours that feel like they belong together rather than being pulled from separate palettes. If your primary is a vivid blue, you get punchy semantic colours. If it is a desaturated slate, the derived colours scale down in energy but stay within clamped ranges that ensure red still reads as danger, green still reads as success, and amber still reads as caution.
Contrast and accessibility
You choose a contrast algorithm (WCAG 2.x or APCA) and a target level (AA Large, AA, or AAA). The tool then walks each primitive scale to find the first foreground shade that clears your threshold, falling back to the neutral scale or to black and white if nothing in the colour's own scale qualifies.
Both algorithms are trying to answer the same question, but they model human vision differently and disagree more often than you'd expect. WCAG is simple and widely adopted; APCA is newer, perceptually grounded, and aware of things like polarity and text size that WCAG ignores entirely.
WCAG 2.x
A simple luminance ratio: (L₁ + 0.05) / (L₂ + 0.05). Widely adopted, legally referenced, and easy to communicate as a single number. The tradeoff is that it treats dark-on-light and light-on-dark identically, and has no concept of text size or use case. Thresholds are 3:1 for large text, 4.5:1 for AA, and 7:1 for AAA.
APCA
The Advanced Perceptual Contrast Algorithm models how humans actually see contrast. The same two colours score differently depending on which is foreground and which is background, because light text on dark is genuinely perceived differently to dark text on light. WCAG would give both the same ratio. APCA also scores by use case: Lc 45 for large text, Lc 60 for body text, Lc 75 for high contrast, and Lc 90+ for fluent reading.
Contrast targets
| Use case | WCAG | APCA |
|---|---|---|
| Large text (18pt+) | 3:1 | Lc 45 |
| Body text | 4.5:1 | Lc 60 |
| High contrast | 7:1 | Lc 75 |
| Fluent reading | — | Lc 90+ |
WCAG defines two levels (AA, AAA) with a separate large-text exception. APCA uses a continuous scale with specific Lc values for each use case, including a “Fluent” tier for optimal readability that WCAG has no equivalent for.
The accessibility score in the sidebar tells you how many of your semantic pairings currently pass, and a colour vision deficiency simulator (protanopia, deuteranopia, tritanopia) lets you verify the system works for everyone.
Export to the coding tool of your choice
The generated system exports as CSS custom properties, Tailwind v4, Tailwind v3, W3C DTCG design tokens, or a Lovable-compatible theme. Semantic tokens reference primitives via var(), so dark mode is handled by remapping references rather than duplicating values.
Teach your AI to use them
Your tokens are useless if your AI assistant doesn't know they exist. Left to its own devices, Claude will reach for bg-blue-500 text-white and you'll spend more time correcting it than you saved generating the palette in the first place.
The skill below fixes that. Drop it into .claude/skills/colour-system-tokens/skill.md and Claude Code will use your semantic tokens properly, reach for primitives only when the semantics genuinely don't fit, and stop hardcoding colours altogether.
---
name: colour-system-tokens
description: Always use design tokens from our design system. Never use arbitrary values, custom colors, or hardcoded pixels.
---
# Colour System Tokens
Always use design tokens from our design system. Never use arbitrary values, custom colors, or hardcoded pixels.
## Colors
Use semantic color tokens, not hex values or primitive scales. Follow this hierarchy:
### 1. Semantic Colors (Use First)
Pay close attention to layering and foreground/background relationships:
**Primary Colors:**
- `primary`: Primary brand color for buttons, links, focus states
- `primary-foreground`: Text on primary backgrounds
- `primary-subdued`: Subtle primary backgrounds (e.g., selected items, hover states)
- `primary-subdued-foreground`: Text on subdued primary backgrounds
- `primary-highlight`: Darker emphasis or active states for primary elements
- `primary-highlight-foreground`: Text on highlight primary backgrounds
**Secondary Colors:**
- `secondary`: Secondary brand color for less prominent actions
- `secondary-foreground`: Text on secondary backgrounds
- `secondary-subdued`: Subtle secondary backgrounds
- `secondary-subdued-foreground`: Text on subdued secondary backgrounds
- `secondary-highlight`: Darker emphasis or active states for secondary elements
- `secondary-highlight-foreground`: Text on highlight secondary backgrounds
**Neutral Colors:**
- `neutral`: Neutral color for tertiary actions
- `neutral-foreground`: Text on neutral backgrounds
- `neutral-subdued`: Subtle neutral backgrounds
- `neutral-subdued-foreground`: Text on subdued neutral backgrounds
- `neutral-highlight`: Darker emphasis for neutral elements
- `neutral-highlight-foreground`: Text on highlight neutral backgrounds
**Intent Colors (Use for Status/Feedback):**
- `success`, `success-foreground`: Positive outcomes, completed states
- `success-subdued`, `success-subdued-foreground`: Subtle success backgrounds
- `success-highlight`, `success-highlight-foreground`: Emphasis for success states
- `destructive`, `destructive-foreground`: Errors, dangerous actions
- `destructive-subdued`, `destructive-subdued-foreground`: Subtle error backgrounds
- `destructive-highlight`, `destructive-highlight-foreground`: Emphasis for error states
- `warning`, `warning-foreground`: Warnings, important notices
- `warning-subdued`, `warning-subdued-foreground`: Subtle warning backgrounds
- `warning-highlight`, `warning-highlight-foreground`: Emphasis for warning states
**Surface & Utility Colors:**
- `background`: Page background
- `foreground`: Primary text color
- `card`, `card-foreground`: Card/panel backgrounds and text
- `popover`, `popover-foreground`: Popover/dropdown backgrounds and text
- `muted`, `muted-foreground`: Muted/disabled backgrounds and secondary text
- `accent`, `accent-foreground`: Subtle accents (neutral-based, not a brand color)
- `border`: Default borders
- `input`: Input field borders
- `ring`: Focus ring color
### 2. Primitive Scales (Use Sparingly)
Only use primitive color scales when semantic tokens don't fit:
- `primary-50` through `primary-950`: Full primary color scale
- `secondary-50` through `secondary-950`: Full secondary color scale
- `neutral-50` through `neutral-950`: Full neutral color scale
- `success-50` through `success-950`: Full success color scale
- `destructive-50` through `destructive-950`: Full destructive color scale
- `warning-50` through `warning-950`: Full warning color scale
- Use these for custom gradients, illustrations, or unique design needs
**Example:**
❌ Don't: `bg-primary-500 text-white`
✅ Do: `bg-primary text-primary-foreground`
❌ Don't: `bg-green-100 text-green-800`
✅ Do: `bg-success-subdued text-success-subdued-foreground`
❌ Don't: `bg-red-50 text-red-700`
✅ Do: `bg-destructive-subdued text-destructive-subdued-foreground`
Built on OKLCH
Everything runs through OKLCH colour space, where equal numeric steps produce equal perceptual steps. RGB and HSL cannot guarantee this, so a lightness curve that looks smooth in HSL often produces visible jumps in practice. OKLCH solves this, and it is the reason the generated scales look balanced without manual tweaking.
The tool detects whether your input is chromatic or achromatic and applies a different lightness distribution for each. Chromatic colours get vibrant mid-tones with eased transitions at the extremes. Achromatic colours get a more linear spread optimised for surface layering and text contrast.
Adaptive lightness
Lightness distribution scales based on the headroom above and below your base colour. Light shades use smooth easing curves tuned for UI backgrounds, while dark shades take larger steps to guarantee contrast for text.
Adaptive Lightness Distribution
This means a dark base colour and a light pastel both produce usable scales, without the light end collapsing into white or the dark end bottoming out.
Progressive chroma
Chroma follows an easing curve from near-zero at shade 25 up to full saturation through the 400-900 range, keeping light tints clean enough for backgrounds while interactive shades stay vivid.
Hue stays constant across the entire scale, with no Bezold-Brucke shift or artistic hue rotation, so your brand colour at shade 500 is the exact colour you picked.