A design token taxonomy for Checkout.com
I spent 6 months at payments Fintech checkout.com, paying down some tech debt to lay the foundations for a design system. The most enjoyable project was working on a design token taxonomy with Dave Musson. Unconsciously, we went through a process of deciding on a simple approach, second guessing ourselves and going really wide to something huge and complicated, then realising it wouldn’t scale and reverting back to something simple again. One of the design system principles we’d set ourselves was:
"We do the work up front to make the design system easy to use".
It was good to keep coming back to this promise and asking each other if someone new would be able to consume this taxonomy without our help. We met weekly with product designers and engineers to check our thinking.
I’m not sure our taxonomy is particularly groundbreaking but I wanted to document it here so I didn’t forget our process. If it turns out to be helpful to you, or saves you the pain of the messy bit in the middle, then that’s super awesome 💙
If you need to learn anything about design tokens, Stu Robson has an excellent cache of design token learning (his occasional newsletter roundup is also gold). It’s easy to get overwhelmed with the sheer volume of posts and opinions on how to structure design tokens. We shared a lot of reading and pasted up a Figma board with the token taxonomy from the established design systems.
- The most thorough article on naming design tokens is Nathan Curtis’s Naming Tokens in Design Systems. Dave and I must have seriously bumped up the hitcount on this post. Many subsequent posts you read are just a rehash of this. Nathan’s articles have been like a bible to me over the years, his Medium articles from 2017/8 are still relevant. Nathan - the design systems community owes you MUCH BEER 🍺
- Naming design tokens by Lukas Oppermann. This unstuck us when we couldn’t find terms that would describe the token or a grouping that would scale.
- Radix-UI. Woah - what an elegant, scalable colour system! If your org's palette works off neat colour ramps (rather than a selection of legacy values eyeballed by an agency 🙄) then this colour system is super scalable and easy for consumers to learn. You could also bake in colour contrast accessibility; values 11-12 meet a 4.5:1 and could be used for text, values 6-8 for icons as 3:1, etc). This meaningful weights approach is also documented here by Kevin Muldoon. I love the predictability. Much to learn.
What to tokenise
For V1, we decided to tokenise:
- Border radius
We figured we couldn’t build a UI without these elements and we could leave fancy stuff like animation to figure out in V2.
Baking in scale
The system will initially cover product but is intended to scale across different parts of the estate. The org will undoubtedly grow, potentially rebrand and sprout verticals so we needed to bake in scalability. Option tokens provide a level of abstraction that allows you to change values without causing breaking changes.
Figuring out a taxonomy took us from Figma, though a spreadsheet and back to Figma again. Much as I mocked our spreadsheet it was an incredibly useful thinking step.
We initially tried give our option tokens meaningful names and this is where it got messy. Every time we came out with a set, we realised we had effectively written decision tokens. We thought that meaningful names would make option tokens easier for consumers to apply but it just resulted in multiple tokens that all had the same colour values. We eventually agreed we were trying to be too clever and reverted to giving option token colour names and ramp values; for colours: purple.20, purple.100, etc.
Nathan Curtis’s taxonomy table provided a good initial framework. A few folks have recently expanded this as a Figma community file. We started with 4 categories. This simple taxonomy scaled until we hit CTAs and links, then we realised we had to expand to fit in the variants and states.
We were starting off with a single light theme but reserved the Theme column to add a dark theme or different product themes in future. Adding in scale and shape, we ending up with 9 groupings in total.
Decision tokens communicate purpose and, staying true to our principle, we wanted consumers to recognise their application, without lengthy explanation. We started with component-specific tokens (under groupings: forms, buttons, tabs, etc) but quickly realised this was a pathway to literally hundreds of tokens that all contained the same colour values. Was searching through hundreds of tokens that were visually the same really the easiest path for our consumers? We rolled back from this and finally hung the the taxonomy hung off 3 token groups to specify intent. These properties are the only thing a consumer needs to learn:
- base: for default UI like canvas, page, cards and tables
- control: for controls you interact with like form fields and navigation
- action: for calls to action like buttons and links
In addition, we settled on these groupings for colour tokens:
- usage: the thing you’re applying the style to
- intent: the semantic intent
- variant: flex for future scale - primary and secondary are standard but we added emphasis and decorative for use in components like notifications and alerts.
- state: for CTAs
- mode: currently just an invert of the single theme - use for elements like buttons on a dark background. This could potentially give space to an -onDark or -orLight mode if you wanted to bake a dark mode into one theme. I'd prefer to create a separate dark theme.
The only way to test this taxonomy would grow and scale was to apply it to all the existing components and try to anticipate edge cases. CTAs, tabs and semantic states/alerts proved the best stress tests.
There are so many different way to cut this and each method has known gotchas. Because the system had to scale across multiple platforms, we wanted to avoid semantic names like heading levels. An H1 on the website is 5 rem, while an H1 in product is 1.5 rem. Numbered systems are tidy and they’re ideal you don’t anticipate change. But what happens if a new vertical comes along that needs to insert a value between 4 and 5? Same goes with T shirt sizes.To get around this came out with these categories:
- display (large typographic headings on marketing stuff)
- label (for forms, eyebrows, data)
- code (code snippets for docs)
- tabular (used in product tables to align numbers)
- legal (small print)
Then adding t shirt sizes within each category:
We figured if some new hotness comes along like data viz they can potentially add a new category (like charts, so type.chart.md) and within that have 5 values to play with. This allowed us to use the T shirt sizes everyone recognises, with room for future scale.
Inspired by Bulb, we decided to give our spacing tokens names. This allows scale as you’re not tied to a number or t shirt size. Spacing values were potentially open to change - we could anticipate relaxed/loose themes for marketing and compact/dense themes for product.
The tech writing team came up with 30 names beginning with S for spacing, and we ran card sorts with groups of designers and engineers. Is a swan bigger than saxophone? Amusingly, the engineers just did the card sort while the designers left lots of comments about the words they had issues with 🙃
We came out with 10 spacing values from spacing.seed (.25rem) through to spacing.saturn (7rem) with values to spare in between each jump for future additions.
Stroke, radius and elevation tokensTo keep it simple we numbered and t shirted these values as they were less likely to need future scale.
Putting it all together
Here’s what we came out with across all criteria. It’s a rare opportunity to work on a taxonomy from scratch so a brilliant learning experience. I’ve left Checkout but plan to check in with Dave and see how this scales to fit other themes.
Update: I updated some groupings after a catchup with Dave Musson - he'd tweaked the structure and suggested better column headings. He's taken some of the categories in a slightly different direction but I thought I'd park this as the work above is where my contribution ends.