Design systems · Stage 4 of 6 · Writing the colors into a design system
Two lessons on writing the color roles into Figma, a repository, and the rules page.
In this article, we will cover the following:
These are lessons 8 and 9 of the twelve it takes to build a design system. Lesson 1 was the first article, Introduction to design tokens. Lessons 2 to 5 were the second, Choosing the color roles. Lessons 6 and 7 were the third, Expanding on color roles. Lessons 10 to 12 come in the next article. The full list is below, each with an example.
Learn what a design token is and the words used for its parts, so the rest of the steps make sense.
One place where people edit the colors, one file that is the true copy, and copies made from it for every place that needs them.
Write down what your screens need to tell users. Each line is one purpose.
For each purpose: how many versions, is it shown with color, does it have a set of equals. Add up the color families.
Pick the one color that stands for each family. This is the only step you do by eye.
Make the fill's family, its lighter and darker versions, then choose one for the background, the border, the icon, the words, the fill of a badge, and the words on it, check each one for contrast, then name each green for how loud it is.
Write the six choices down in one place: the color role, the part, the green, and what it was checked against.
A program turns the names into the files the website uses. Nobody types them by hand.
Describe each name and when to use it, so that people and tools can look it up.
A tool that stops anyone from typing a color by hand when a name exists for it.
Go through the screens that already exist and replace every hand-typed color with its name.
Open each screen, in light and in dark, and check with your own eyes that it looks right.
Article 3 ended with lesson 7: six choices for the success role, written down in one table by hand. The color role, the part, the green, and what it was checked against. This article takes that table and writes it into Figma, by hand and with an AI agent, then lets a program write the files from it.
Two ways to write it: by hand, and with an AI agent. Both start after lesson 6, with the greens already chosen. What follows is writing them down, in two steps: fill out the list, then write the list into Figma.
By this point the role is chosen, in article 2, its family is made, in article 3, and the greens it uses have their loudness names from article 3's 5th step. Start with a blank list for the role: a row of sixteen boxes for the family at the top, then five columns: the color role, the part, the meaning, dark, and sits on. The parts are the same for every role. Most design systems give each color role these six: a background, a border, an icon, the words, a filled background for small solid shapes such as a badge, and the words on that filled background. The last row is for anything extra your screens need.
| Color role | The part | Meaning | Dark (optional) | Sits on |
|---|---|---|---|---|
| Background | ||||
| Border | ||||
| Icon | ||||
| Words | ||||
| Background (filled) | ||||
| Words (filled) | ||||
| Part (inverted), optional | ||||
| Additional (custom) |
A row of sixteen empty boxes numbered 1 to 16, labeled family, then a meaning row with five names, subtlest, subtler, subtle, default, and strong, each with an empty field for a shade number, above a blank table with eight rows and five columns: the color role, the part, meaning, dark, marked optional, and sits on. Each cell in the Dark column holds a faded, empty field. Only the part column is filled in: background, border, icon, words, background (filled), and words (filled), then a faded row called part (inverted), optional, then a last row in italics called additional (custom).
This list is also a page you can fill in: the color role worksheet. Type the codes into the boxes, fill the rows, and copy or download the result.
Fill in the blanks. The family row takes the sixteen greens from article 3, with their codes. The part section has the standards of every system. You can change this to your custom work. In this example, the color role is success. The Meaning column is the loudness name from article 3's 5th step: subtler for the background, subtle for the border, default for the icon and the badge fill, strong for the words, subtlest for the badge words. No green number is written; the name stands for it. Sits on is what you checked contrast against in lesson 6: the background sits on the page; the border, icon, and words on the background; the badge fill on the page; the badge words on the fill. When a color changes, every row that sits on it gets checked again.
The inverted row is optional. Inverted, from article 1, is a part on a surface opposite to the screen, such as a dark toast in a light app. Fill this row only when a colored accent, such as a green check, sits on a dark patch: Icon (inverted), the meaning name of its green, and the dark patch under sits on.
| Color role | The part | Meaning | Dark (optional) | Sits on |
|---|---|---|---|---|
| success | Background | subtler | the page | |
| success | Border | subtle | the background | |
| success | Icon | default | the background | |
| success | Words | strong | the background | |
| success | Background (filled) | default | the page | |
| success | Words (filled) | subtlest | the filled background | |
| Part (inverted), optional | ||||
| Additional (custom) |
The same table filled in for success. Above it, the sixteen greens numbered 1 to 16, each box holding its number and its color code, then the meaning row: subtlest 1, subtler 3, subtle 5, default 12, strong 14, each with a swatch of its green. The Dark column is still faded and empty. The six standard rows each give a meaning name with a swatch of its green: subtler, subtle, default, strong, default, and subtlest. The last column says what each part sits on. The part (inverted) row stays faded and empty, and the additional (custom) row is left blank.
In lesson 2 you chose one author: the one place where the colors get edited. Every other tool may read the colors, but only the author writes them. Here is that choice again, with the option this article uses marked.
The Author card from article 2: three options with a round button each, design tool, code editor, and token platform. The first, design tool, is selected, and Figma Variables is in bold among its examples.
The list from step 1 works with any of the three, because it records decisions, not a tool. In this article the author is Figma, a design tool, so step 2 shows how to write the list into Figma.
Figma keeps named colors as variables, and groups them in collections. Writing the list into Figma takes three collections, made in this order: the family first, the sixteen greens, so every green has a name; then the meaning, the five loudness names from article 3, each applied to its green; then the role, success, with one variable for each row of the list, each applied to its meaning. Each collection points at the one before it, so a change to one green in the family, or to one meaning, reaches every place that uses it.
Article 3 showed how to make the family. This is the one we will use here.
Shopify's sixteen greens in one row, numbered from 1, the lightest, to 16, the darkest. Values from Shopify's published token files.
Each of the sixteen becomes one variable in Figma, named by its number.
A drawing of Figma's Variables view just after the plus next to Collections is clicked. The plus is circled. A new collection called Collection sits in the list with its name selected in a blue box, ready to rename. The main area says there are no variables in this collection yet, with a blue Create button and an Import button.
You do this once per family. Success, and anything else that uses green, shares it.
The same view after the greens are added. Green family is the only collection, holding 16 variables. The main area is a table with a Name column and a Value column: green-1 to green-16, each with a small color icon before its name and a swatch and color code as its value. Rows 6 to 11 are folded to save space.
The five loudness names from article 3's 5th step become a collection of their own, between the family and the role. Each name is applied to its green, so the role never has to touch a green directly.
success/subtlest. Press Return, and Figma makes a success folder with subtlest inside it. Repeat for success/subtler, success/subtle, success/default, and success/strong.The sixteen greens from the 1st part, with the five the role uses outlined. Under each outlined shade, its loudness name and the part it goes on: green 1 subtlest, the badge words; green 3 subtler, the background; green 5 subtle, the border; green 12 default, the icon and the badge; green 14 strong, the words.
The Variables panel with a second collection, Meaning, selected under Green family. Under a success group are five variables, subtlest, subtler, subtle, default, and strong, each showing the name of the green it is applied to: green-1, green-3, green-5, green-12, and green-14.
Five names for five greens, once per role.
The Variables view as the role's collection is made. The plus next to Collections is circled. Green family and Meaning are already in the list, with 16 and 5 variables, and under them a new collection called Collection sits in a blue box with its name selected, ready to be renamed Success. The main area says there are no variables in this collection yet.
static/bg/success/default. Article 1 explains each part of these names.Type the name into the new row with its slashes, as one piece, and press Return.
A drawing of the Success collection with one new color variable. Its name box is open with a blue outline, and the name static/border/success/default is being typed into it. Its value shows success/subtle from Meaning, which step 3 applies. Under Groups, All is selected.
Figma turns each slash into a folder. One name makes three, one inside the other: static, border inside it, and success inside that. They appear under Groups, and the table shows the folder path as a heading, with only the last word, default, in the row. The next names fill in the same tree: static/bg/success/default adds a bg folder beside border, and static/bg/success/filled lands under the same heading as it.
The same view after pressing Return. Under Groups, three new folders are shaded yellow, nested one inside the other: static, border, and success. The table shows a heading, static / border / success, with one row under it named default, holding success/subtle.
The same view after the role is added. Three collections are listed, Green family with 16 variables, Meaning with 5, and Success with 6, and Success is selected. Under Groups, a nested list: All and static hold 6; under static come bg, border, icon, content, and label, each with a success group inside it. The table has a heading for each group: static / bg / success, with default and filled under it; static / border / success, static / icon / success, and static / content / success, each with default; and static / label / success, with filled. Each value is a gray chip with a swatch and a meaning name such as success/subtler, where the family's table showed color codes.
The chip is the point of the 3rd part. A value picked from Meaning shows the meaning's name, not a green and not a code, so the Success collection says exactly what the list says. Click the chip and Figma shows the meaning; click that, and it shows the green. If success ever has to change, the five lines in Meaning change and every place follows.
Three ways to work with AI here, using different methods and tools.
Start with the list
For a designer who filled out the list by hand. Steps 1 and 2 below.
Start with the drawing
For a designer who drew the component in lesson 6 and named its layers. The agent reads the layers and writes the list for you. Not covered here.
Two columns of boxes joined by arrows. Left, starting with the list: you fill out the list, the agent makes the collections. Right, starting with the drawing: you draw and name the layers, the agent writes the list, the agent makes the collections. Boxes for what the agent does are shaded purple.
This article will focus on 1, connecting an MCP to Figma.
Same list from Step 1 by hand, filled out on the color role worksheet. Copy it as text or download the PDF for step 2.
The same three parts, 1st the family, 2nd the meaning, and 3rd the role, in one request, with the sheet from step 1:
The Variables panel after the agent's request: Green family with 16 variables, Meaning with 5, and Success with 6, with Success selected and the same groups and rows as the by-hand version above.
Check the panel against the by-hand pictures above. What you no longer do is type the variables.
The list is the record, and the three collections carry it into Figma. Lesson 8 lets a program turn the names into the files screens are built from, and lesson 9 writes the rules for using the names where people and tools can both read them.
Repeat the two steps for the other color roles, if not done so yet.
Lesson 2 decided where the colors are kept: one place people edit, and copies made from it. Here is that choice again, with the option this article uses marked.
The Store card from article 2: three options with a round button each, the design file itself, Git repository, and platform database. The second, Git repository, is selected.
This is where the handoff occurs. The three collections in Figma are a working system for a design team, and they are all a developer needs. Two ways to hand them over:
The designer's role stops at the handoff. What follows is usually a developer's job. For the sake of this blog, we'll continue into how it gets set up in a repository: a shared folder of files kept by a version control tool such as Git, where every change is recorded with who made it and when, and can be reviewed before it lands. In a company it is standard practice, for three reasons.
From here on this is the developer's work. This is where the actual colors are pulled from to develop the product: the website, or the app. If you are the designer, read on to know what happens to your file.
A developer who has worked with tokens will set up the program below. On a team that has not, ask about this part, otherwise it might sit in the repository while the codes keep being copied by hand. After that, each new file is the whole handoff.
The one place is the token file from the handoff: one small JSON file, plain text with names, values, and braces, in three parts, one per tier. It is written in the format the design tokens standard describes, so that other tools can read it too. It goes in the repository as it is. Here is the background of the "Saved" message in all three parts.
"green": {
"3": { "$type": "color", "$value": "#CDFED4" },
"15": { "$type": "color", "$value": "#073630" }
}The only part with color codes in it."success": {
"subtler": { "$type": "color", "$value": "{color.green.3}" },
"subtle": { "$type": "color", "$value": "{color.green.5}" }
}Every value is a pointer at tier 1, written in braces."static": { "bg": { "success": {
"default": { "$type": "color",
"$value": "{color.success.subtler}",
"$extensions": { "mode": { "dark": "{color.green.15}" } }
}
} } }The value points at tier 2. The dark shade, from the Dark column, points at tier 1.Three parts of one file side by side. The first holds two greens with their color codes. The second holds two meaning names, each pointing at a green in braces. The third holds one place name whose value points at a meaning name, with a dark shade under it pointing at a green.
And the whole file for the success role, with the family cut to the greens the role uses.
One JSON file in a dark code box. Under color, three groups: green, with nine shades and their codes; success, with five meaning names each pointing at a green in braces; and static, with the six place names nested by part, each pointing at a meaning name. The background, border, icon, and words each carry a dark entry under their value, pointing at green 15, 13, 8, and 6.
The developer puts the JSON in the project, in a folder usually called tokens, and records it with three commands: add the file, commit it with a message that says what changed, and push it to the shared copy.
Three commands in a terminal: git add with the file's path, git commit with a message in quotes, and git push.
Then a pull request: a page where another developer sees the change, line by line, and approves it before it lands. Every later change to the file takes the same path, add, commit, push, review, so the history and the review from the three reasons above come for free.
The website consumes a style file, not the JSON. Lesson 2's last card, Consume, listed what gets made from the store. This article makes two of them: the Web CSS here, and the docs in lesson 9.
The Consume card from article 2: four options with a square check box each, Figma, docs, web CSS, and iOS and Android. Docs and Web CSS are checked.
The program, in this case we will use the example of Style Dictionary, a free tool made for exactly this, is short. It reads the file, follows every pointer until it reaches a color code, and writes the style file: one block of lines for light screens and one for dark. The output goes in a folder nobody edits. If a pointer points at a name that does not exist, the program stops and says which one. That is the cheapest check in the whole system, and it catches the typo that a hand-typed file would carry for months.
Three steps get from the repository to a .css file.
npm install style-dictionary. It is now a tool the project carries, like any other.A config file in a dark code box: a source line pointing at tokens/tokens.json, and a css platform that writes variables.css into a build folder.
npx style-dictionary build. It reads tokens.json, follows the pointers, and writes build/css/variables.css. The dark block comes from the dark entries, written as a second pass with a few more lines of config, and the agent writes those. The website links variables.css like any other style file. Run it again whenever tokens.json changes, by hand or as part of the site's build. Or ask the agent in the project to do all three.Here is what it writes for the success role.
A style file excerpt in two blocks. The first block, for light screens, lists the six success place names with their color codes. The second block, for dark screens, lists the same six names with different codes, except the badge fill and its words, which stay the same in both.
Now change the green in one place. Edit the code next to green 3 in tokens.json, run the program, and every screen that uses the background of a success message is rewritten. Nobody searches the website for the old code. Nobody misses one. The row in lesson 7 was a decision; this is what makes the decision stick.
The names exist and the files exist. To help everyone use them the same way, one page says when to use each name, and when not to. The list recorded where each color goes. Lesson 9 writes the rules for reaching for it: which name for which job, what is forbidden, and what to do when the name you need is not there.
Without this page, the next person to build a screen, a developer, another designer, or an agent, types a color code, such as #047B5D, instead of the name.
The page has two types of users. A person skims it before building a screen. A tool reads it before writing one. It is saved in the repository as design-system.md, next to tokens.json, so people and tools both find it. Here is the one for this series' color roles.
Quick reference · color
Never a color code in a screen. Use a place name:
--color-{static|action}-{bg|border|icon|label|content}-{role}-{version}
static is for things you look at: messages, badges, cards. Its version is default, or filled for a small shape filled solid, such as a badge, and the words on it, or inverted for a part on the opposite background.action is for things you press: buttons and links. Its version is default, hover, or pressed.bg is the fill, border the edge, icon the mark, label short words on the interface, content the words people read.success an action finished or a status that is good · warning needs attention, does not block · critical impossible, blocked, or an error · info worth noticing · neutral carries no meaning.Light and dark live inside the name. Do not write a second color for dark screens. If a color is unreadable in one mode, the fix is the dark entry in the token file, not the screen.
The old names are gone. There is no green, no #047B5D, and no ok in a screen. Those were the names before the system. Do not bring them back.
Missing a name? Add it to the token file, run the program, then use it. Never type a value "for now".
A one-page quick reference. A rule in bold, the name pattern with its five slots, four lines explaining the slots and the roles, and three more rules: light and dark live in the name, the old names are gone, and what to do when a name is missing.
Take each row of your Lesson 7 table and write it three times: the shade by family and number, the meaning it carries, and the place it is used, with the five-part name from article 1 and, if your screens go dark, its dark shade. Keep the three tiers in one small file, and let a program follow the pointers and write the style file, so that changing a shade in one place changes every screen that uses it. Then write one page of rules: the name pattern, the roles in a line each, what is forbidden, and what to do when a name is missing. And answer the question that starts article 5: now that the names exist, how do you make sure every screen, old and new, actually uses them?
Next · Stage 5 · Implementing consistency of the design system: a checker that holds back a hand-typed color, the existing screens changed over to the names, and a human check before release.
The design system series