Colour Systems
Date: 2026-08-16
A structured palette plus the semantic names that point into it. The structure exists so contrast is guaranteed rather than checked, and the naming exists so a rebrand doesn’t require rereading every use.
A colour system is a set of colour scales, a set of role-based names mapping onto them, and rules about which combinations are permitted.
Three parts, and most “colour palettes” only have the first.
Scales, not colours
A brand colour alone is unusable — you need the same hue at many lightnesses for borders, backgrounds, hovers and text.
blue.50 lightest — page backgrounds
blue.100 subtle fills
blue.200 borders
blue.300
blue.400
blue.500 the brand colour
blue.600 hover / darker action
blue.700
blue.800
blue.900 text on light backgrounds
blue.950 darkest
Numbering by lightness rather than by order is what makes scales interchangeable — red.600 and blue.600 are equally dark, so swapping one for another doesn’t break contrast.
Generating them properly
Naive scales — lightening and darkening in sRGB — produce steps that look uneven and hues that drift.
sRGB manipulation
perceptually uneven steps
hue shifts, especially in blues
no guarantee about contrast
OKLCH (lightness, chroma, hue)
L maps to perceived lightness
even steps look even
hue stays put when L changes
OKLCH is the practical reason to care about colour spaces. Fix the hue, step the lightness evenly, and the scale is perceptually uniform — which means a fixed numeric step also gives a predictable contrast step.
Semantic naming
The argument, stated plainly:
--red-500 what the colour IS
--colour-danger what the colour is FOR
Components reference the second. The first becomes an implementation detail of the theme — Design Tokens.
The test for whether you’ve named correctly: could the value change without the name becoming a lie? --colour-danger can become orange. --red-500 cannot.
Roles worth defining before anything else:
surface page and card backgrounds
text default, muted, inverse
action primary interactive
border default, strong, subtle
focus the focus indicator
danger · warning · success · info
Contrast is the constraint, not a check
Text and interactive elements have minimum contrast requirements — 4.5:1 for body text, 3:1 for large text and for UI component boundaries under WCAG AA.
Build the guarantee into the scale. If 600 and above always clear 4.5:1 on your lightest surfaces, “which blue for this text” stops being a question anyone has to answer.
on white
blue.400 2.6:1 ✗ fails body text
blue.500 3.9:1 ✗ fails body, ok for UI
blue.600 5.2:1 ✓
blue.700 7.4:1 ✓
Publish that table. A palette shipped without contrast figures guarantees somebody uses blue.400 for body text — WCAG, Inclusive Design.
Dark mode is not inversion
Flipping lightness produces harsh, over-saturated results, because dark surfaces need lower chroma and because pure white on pure black causes halation.
LIGHT DARK
surface white near-black, not #000
text near-black off-white, not #fff
action blue.600 blue.400
↑ LIGHTER, because
contrast reverses
The semantic layer is what makes this cheap — same names, different mappings. Without it, dark mode is a second stylesheet — Theming.
Where it goes wrong
- Too many scales. Eleven steps across nine hues is ninety-nine colours nobody can hold in their head. Most systems need one brand scale, one neutral scale, and four status colours
- Neutrals ignored. The grey scale carries more of the interface than the brand colour, and an untuned grey scale is what makes a UI look cheap. Slightly tinting neutrals towards the brand hue is a reliable improvement
- Colour as the only signal. Status shown by colour alone excludes colour-blind users and fails in greyscale — pair it with an icon or text
- No focus colour. It gets picked ad hoc, and it’s the one colour with a hard accessibility requirement