A colour scale is a named, numbered set of steps for a single hue — the blue-50 through
blue-900 pattern that nearly every design system converges on. It exists so that decisions
get made once, centrally, instead of being re-improvised every time someone needs a slightly
darker border.
Why numbered steps
Naming colours by appearance falls apart immediately. light-blue and lighter-blue leave no
room to insert something between them, and blue-2 tells you nothing about whether it is
light or dark. Numbering by lightness solves both: the number means something, there is always
room to interpolate, and a designer and a developer can discuss blue-600 without ambiguity.
The near-universal convention runs 50 (lightest) through 900 or 950 (darkest), in steps of 100 with an extra step at the light end. Low numbers are backgrounds, high numbers are text.
What each region is for
- 50–100. Page and callout backgrounds, hover states on white, selected rows.
- 200–300. Borders, dividers, disabled surfaces.
- 400–500. The base brand colour usually lands here. Icons, non-text graphics, chart fills.
- 600–700. The workhorse for text and interactive elements on light backgrounds. This is where your primary button colour typically sits, because it needs to pass contrast against white.
- 800–950. Headings, high-emphasis text, and surfaces for dark mode.
A useful discipline: the same step number should mean the same lightness across every hue in
the system. If blue-600 and green-600 have noticeably different lightness, swapping one
for the other breaks layouts, and that is precisely what the numbering promises you can do.
Perceptual uniformity is what makes that promise keepable, which is why scales should be
generated in OKLCH rather than HSL — see the converter on why HSL lightness
misleads.
Getting contrast right by construction
Design the scale so that contrast is guaranteed rather than checked afterwards. A common and effective target:
- Step 600 or higher passes 4.5:1 against step 50 and against white.
- Step 500 passes 3:1 against white, so it can be used for borders and icons.
- Step 100 passes 4.5:1 against step 800, giving you a working dark-mode pair.
Fix those anchor points first and interpolate the rest between them. A scale built this way lets anyone pick a pair by arithmetic — “text at 700 on background at 50” — instead of running a checker every time.
Neutrals are the hard part
Grey scales carry more of an interface than any brand colour, and pure neutral greys often look lifeless next to a saturated palette. Adding a small amount of chroma in the direction of your primary hue — a barely perceptible amount — makes the greys feel intentional and part of the same family. Too much and everything looks tinted; the useful range is narrow.
Where people go wrong
Even lightness steps. Perceptually even is not the same as numerically even, and the light end usually needs finer gradations than the dark end because that is where subtle background differences do their work.
Too many hues. A primary, a neutral, and three or four semantic colours (success, warning, danger, info) covers almost everything. Every extra hue multiplies the maintenance and the opportunities for inconsistency.
Scales that do not survive dark mode. Inverting a scale rarely works directly — perceived contrast behaves differently on dark backgrounds, and saturated colours look brighter. Plan both modes when you build it, not after.
Hard-coding steps in components. Reference semantic tokens (--color-text-primary) that
point at scale steps, rather than using blue-700 directly. Then rebranding is a change in
one file.
Our guide on colour scales for design systems works through a full example, and export will give you the result as CSS custom properties, Tailwind config or design tokens.