Building a neutral colour palette for UI: tinted greys, and how many you need
Most of what's on a screen is neutral: backgrounds, text, borders, disabled states. In a good neutral colour palette, UI greys form a short ramp, each with a job, tinted slightly toward the brand's hue so they feel related to it. A small site needs about six; a product interface perhaps ten to twelve. Each has to be defined again, not inverted, for dark mode.
Why neutrals matter more than accents
Brand colours get the attention, but a page is perhaps nine-tenths neutral. The ground, the card behind a price, the hairline between list items, the grey of a caption: these set the temperature of the whole design. A page whose greys were picked one at a time, a cool grey here and a warm one there, looks slightly off even when nobody can say why. A deliberate grey scale for UI fixes that by deriving every neutral from one hue and changing only lightness.
Warm vs cool greys
| Tint | Feels | Suits | Pairs with |
|---|---|---|---|
| Cool (toward blue) | Precise, technical, clean | Software, clinics, finance | Blue, teal, amber actions |
| Warm (toward yellow or red) | Soft, crafted, inviting | Food, hospitality, craft, editorial | Red, gold, green actions |
| Green-grey | Natural, calm | Wellness, gardens, outdoor | Sage, clay, moss |
| Pure grey | Neutral, graphic, strict | Design studios, galleries | One strong colour, such as red |
The tint should be faint. A grey with too much hue stops being a neutral and starts competing with the accent. In a colour space like OKLCH, which MDN's oklch() reference describes in terms of lightness, chroma and hue, that means a chroma of roughly 0.005 to 0.02 at a fixed hue, with lightness doing all the stepping. Because OKLCH lightness follows perceived brightness more closely than HSL's, equal steps in L look like equal steps to the eye.
How many steps
Name neutrals by what they do, then count. A marketing site or a small business page needs roughly these:
- Ground: the page background.
- Surface: cards, panels and alternate sections, one step away from the ground.
- Line: borders and dividers. Often the text colour at low opacity, so it adapts to whatever it sits on.
- Muted text: captions, metadata, secondary labels. It still has to pass 4.5:1 against the ground.
- Text: body and headings, the darkest step in light mode.
- On-action: the text colour that sits on the action colour, usually the ground colour itself.
A product interface adds steps for hovered and pressed surfaces, a raised surface for menus, a disabled text tone and a stronger border for inputs. Beyond about twelve, people start choosing between two nearly identical greys and the system loses its point. Design tokens explained shows how to store these as named values.
Contrast rules for neutrals
- Text tones: 4.5:1 against every surface they appear on, under WCAG's contrast minimum. Check muted text on the surface colour, not just on the ground, because that's where it usually fails.
- Borders that identify controls: an input's outline, a checkbox's edge. WCAG's non-text contrast asks for 3:1 where the border is the only thing showing where the control is.
- Decorative dividers: no requirement. A faint hairline between list items can be very light.
- Disabled states: exempt from contrast rules, but they still need to be visible as present.
Neutrals in dark mode
Flipping the ramp end to end rarely works. In dark mode, surfaces get lighter as they rise towards the viewer, so a card is lighter than the ground, the opposite of the usual light-mode shadow logic. Pure black grounds make text glare and shadows invisible; a very dark tinted grey is calmer. Text should be an off-white, and muted text needs re-checking because perception of mid-greys changes on dark grounds. Define a second set of values for each role, keep the same names, and switch the set. Dark mode colour palette covers the full method.
Neutral tokens in Anticode
Each of Anticode's seven design systems defines its neutrals as colour tokens by role: a ground, a surface, text, muted text and a line colour, plus the action colour and the colour that sits on it. Every system has a light and a dark set of the same tokens, tinted to match its character: Technical's greys lean cool toward blue, Editorial's and Luxury's lean warm, Organic's lean toward green, and Swiss uses pure greys from white to near-black. In most systems the line colour is the text colour at low opacity, so it adapts to the ground it sits on; Brutalist uses solid rules in the text colour instead.
You choose Auto, Light or Dark in the visual editor; Auto uses the system's own default set, which is dark for Technical and Luxury and light for the others. In Pro mode the code editor lets you tune the :root colour tokens directly, and those edits are kept with the project.
Questions people ask
Should UI greys be tinted?
Slightly, yes. A faint tint toward the brand hue makes the greys feel related to the accent colour. Keep the tint low enough that they still read as neutral.
How many greys does a website need?
About six for a small site: ground, surface, line, muted text, text and the text on the action colour. Product interfaces may need up to twelve.
What contrast do borders need?
3:1 against adjacent colours when the border is what identifies a control, such as an input. Purely decorative dividers have no requirement.
Sources
- oklch(), MDN Web Docs, checked
- Understanding SC 1.4.3: Contrast (Minimum), W3C, checked
- Understanding SC 1.4.11: Non-text Contrast, W3C, checked