Craft · · 3 min
Building a colour palette you can defend
Neutrals do the work, one accent does the pointing, and every colour needs a job description.
A usable product palette is mostly neutrals. This surprises people who arrive from brand work, where the palette is the identity. In a product the palette is infrastructure, and the accent is a single instrument played sparingly.
Neutrals do the work
Four or five neutrals carry every surface, border, divider and line of text in a product. They are the page background, the card that sits on it, the hairline between rows, the body copy, the muted caption underneath.
Two rules make them behave:
Pick a temperature and hold it. Warm greys or cool greys, not both. Mixed-temperature neutrals are the most common reason an interface looks subtly dirty without anyone being able to point at the cause — a warm card on a cool background reads as a rendering error rather than a choice.
Give each one a role, not a number. "Surface", "surface-subtle", "border-card", "border-hairline" survive being applied by someone who was not there when you chose them. grey-200 does not: nothing about the name says whether it is a border or a background, so it gets used as both, and then you cannot change either.
Most design systems converge on this. IBM's Carbon is a thorough public example of role-named colour and worth reading before you invent your own scheme.
One accent, with one job
Then a single accent, and its job is showing where to act. Buttons. The active tab. The focused input's border. That is the list.
The moment the accent also appears in headings, illustrations, badges and the occasional decorative underline, it stops meaning anything. Users learn the association by repetition, and the association only forms if the colour is boringly consistent. An accent used everywhere is a brand colour, which is a different and less useful thing inside a product.
This has a contrast consequence people discover late. A saturated mid-tone accent — the kind that looks superb on a button — usually fails WCAG's 4.5:1 minimum as text under 18px. Our own accent, #E8511E, lands at 3.6:1 on an off-white background. The fix is not to abandon it but to keep two values: the accent for large type and fills, and a darkened variant for small text. Deciding that once beats rediscovering it in an audit.
While you are there: colour must never be the only carrier of meaning. WCAG's use of colour criterion is the one most often failed by a status dot with no label beside it.
Semantic colours are not decorations
Success, warning and danger are a separate set, and they should not be sampled from the accent. Two reasons.
The obvious one is that if danger is a shade of your accent, a destructive button and a primary button look related, which is the opposite of the message.
The subtler one is that semantic colours are the palette's most reusable vocabulary and the easiest to dilute. Once green means "success", using it for an unrelated marketing chart teaches people it means nothing in particular. Reserve them for state and accept that this costs you some flexibility elsewhere.
Write the job description next to the colour
This is the step that turns a palette into something another person — or an agent — can apply correctly.
Compare these two records of the same decision:
#E8511E
#101010
#F4F4F1
#E8511E primary action only — buttons and focus. Never body text.
#C4400F the same accent, darkened, for accent text under 18px.
#101010 ink. All body copy and headings.
#F4F4F1 surface. Cards and inset panels, never page background.
The first is six unexplained hexes and a lottery. The second generalises: faced with a component nobody has designed yet, you can derive the right answer from the rules rather than guessing which existing screen to copy.
That difference matters most when the thing applying the palette is an agent, which has never seen your product and can only reason from what it is told. Asked for a pricing page with the first list, it will distribute the colours plausibly and wrongly — orange headings, orange links, an orange badge. With the second it has nothing to guess. That is the general argument in how to teach a coding agent your design taste, and the same reason type pairings need their rules recorded, not just their names.
Once the rules exist, getting them to the agent is one block of config — see the docs or the Claude Code guide.
Further reading
- WCAG 2.2: contrast minimum — the 4.5:1 threshold and its exceptions
- WCAG 2.2: use of colour — why a status dot needs a label
- IBM Carbon: colour — role-named colour at production scale
Keep reading
Craft
Choosing type pairings for product UI
A practical way to pick two typefaces that hold up across a landing page, an app and an email.
Working with agents
How to teach a coding agent your design taste
Agents do not lack skill — they lack a source of truth about what you find beautiful. Here is how to write one down.
Opinion
Why every vibe-coded app looks the same
The sameness is not a model limitation. It is what happens when nobody supplies a point of view.