Our design system had 47 greys. Now it has 9 (and some semantic tokens)
How we did the audit, what we named things, and the one fight we lost
Contents
Allora. Last month I opened our Figma library and counted the greys. Not the colours, only the greys. 47 ✨. Forty-seven! Some were different by one hex digit, like #F4F4F4 and #F5F5F5, and both were used in production for "background light". Boh.
This is how we cleaned it, in a fintech app with two squads, one designer (me) and a lot of developpers who are very patient with me.
1. The audit (boring, necessary)
I did not trust Figma for this. I asked our frontend lead to search the codebase for every hex and rgb value:
rg -o --no-filename '#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{3}\b|rgba?\([^)]*\)' src/ \
| tr 'A-F' 'a-f' | sort | uniq -c | sort -rn > colours.txt
312 unique values. The 47 greys in Figma became 61 in code ahah. But the top 9 values covered more than 80% of the usages, which already told us the answer.
2. A scale, then meanings
We kept a primitive scale of neutrals and put semantic tokens on top. Components can only use the semantic ones. Primitives are private.
:root {
--neutral-50: #f7f7f8;
--neutral-100: #eeeef0;
--neutral-200: #dcdce0;
--neutral-300: #c2c2c8;
--neutral-400: #9a9aa3;
--neutral-500: #73737d;
--neutral-600: #565660;
--neutral-700: #3d3d45;
--neutral-900: #18181c;
--surface-page: var(--neutral-50);
--surface-card: #ffffff;
--surface-sunken: var(--neutral-100);
--border-subtle: var(--neutral-200);
--border-strong: var(--neutral-400);
--text-primary: var(--neutral-900);
--text-secondary: var(--neutral-600);
--text-disabled: var(--neutral-400);
}
Yes, there is no 800. Nobody used it. A scale is not a religion 🤌
3. Check contrast before naming anything
Every text token was checked against every surface it can sit on. WCAG AA asks 4.5:1 for normal text and 3:1 for large text. Our old "secondary text" grey was #999999 on white, which is about 2.8:1. It was on every transaction date in the app. For years. Nobody complained, because the people who can't read it don't write to support. They just leave.
4. The fight we lost
I wanted to remove pure black text completely. Marketing wanted #000000 for the big balance number "because it feels more serious". We made a research with 6 users, nobody noticed the difference, marketing still won. Fine. I choose my battles, this was not the hill.
What changed after
- Dark mode became one file: we remap the semantic tokens, the components do not change.
- New screens get reviewed in half the time, because "which grey?" is not a question anymore.
- Developpers stopped asking me for hex codes in Slack. I miss them a little. No I don't ☕
If your library has more than 15 greys, I promise you, nobody chose them. They just happened.
Written by
Giulia Bellandi
Product designer in Milano ✏️ design systems, typography, and fighting for the 4px grid. Ex-agency, now in-house at a fintech. Pasta is also a design system.
Get the best of Dynamo Geeks weekly
One email each Monday with the most useful posts. No spam; unsubscribe in one click.
Comments
Sign in to join the conversation.
Sign in to comment