Design systems · Stage 5 of 6 · Implementing consistency of the design system
Three lessons on a checker, the changeover, and the human check before release.
In this article, we will cover the following:
These are lessons 10, 11, and 12 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 8 and 9 were the fourth, Writing the colors into a design system. 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.
You spent four articles building a system. Three months later you open the live product and the success green is wrong on one screen, a badge is a green you never chose, and the dark mode you specified is someone's guess. The design file says one thing and the product says another. Every review becomes a hunt for colors nobody remembers typing, and the design system exists only as a thought.
This article is how the system stays true after you hand it over: find, fix, look. A checker holds back any color that is not in the design system. You build it once, with an AI agent or a developer, and from then on the checker does the checking. This is about building an automated system that keeps the design system consistent without ongoing work. You still make the design decisions.
By hand
Level 1 · simple
Flag
#047B5D.Leave alone
transparent, which means no color.Level 2 · expanded
Flag
#047B5D, rgb(4, 123, 93).text-green-600.brand-green.dark:text-green-400.--color-static-content-primary.Leave alone
transparent, currentColor, and inherit, which are not color choices.Decide
bg-success/10: allowed, or a new name in the family?Two panels side by side, level 1 and level 2, each headed with a small three-bar icon, one bar green for level 1 and two for level 2, and each holding a list to write. Level 1 flags two things and leaves transparent alone. Level 2 flags five things, leaves three alone, and lists two decisions.
With AI
Article 4 ended with two files handed off to developers: tokens.json, the token file, and design-system.md, the rules page. Make sure to connect them to the AI agent, along with the in-review repository for this step.
Level 1 · simple
It would flag 214 places. Three examples:
#047B5D in OrderBadge.css, line 12. Use --color-static-bg-success-filled.brand-green in Header.css, line 4. That name is not in tokens.json.#FFFFFF in Card.css, line 9. There is no name for white in tokens.json yet. Add one, or tell me to allow it.Level 2 · expanded
I read both files and scanned the app's code. Nothing has been changed.
I would flag:
#047B5D in OrderBadge.css.text-green-600.muted-foreground alone is 120 of them.--color-static-content-primary in LoginGate.tsx, which paints nothing.I would leave alone: transparent, currentColor, and inherit; the library folder; test files.
I need your decision on: plain white, used 38 times; bg-success/10, used 12 times; and an orange with no role, used 5 times.
Two panels side by side, level 1 and level 2, each with a prompt and, under it in purple, an example of what the agent writes back. Level 1's reply gives a count and three examples, the last one a question about white. Level 2's reply lists what it would flag with a count for each, what it would leave alone, and three decisions it needs from you.
What it is
The checker is a set of rules added to the checking tool most apps already run to catch mistakes in their code. Your list from step 1 becomes those rules.
What it does
Every time someone saves or pushes code, the checker reads the app's code and stops at any color on your list. It names the file, the line, what is wrong, and what to use instead. A typed code that matches one name is corrected on its own. Anything else is held back until a person decides, so the color never reaches the product.
Normally, this is where a developer steps in. If they have the bandwidth, follow the steps for the handoff. Otherwise, continue here.
When a designer would do this
With an AI agent, a designer can now help build the checker. Why would a designer want to?
A developer still reviews and puts it into the app.
The steps for a designer, with AI
This turns your list into a checker and shows you what it would find, without changing anything in the app. Once a developer turns it on, it runs on its own every time someone saves a change. It will automatically correct a typed color code to match the naming convention in the design system. It offers the best guess, and a person decides, when the code does not match a color in the design system or more than one name could fit. Only a color that is not using a name from the design system is held back from the product.
Other prompt ideas:
I built the checker. Nothing in the app has changed, and nothing was installed.
New file
stylelint.config.js, your rules: no color typed as a code, and no name that is not in tokens.json. It also holds the part that finds the closest name for a typed code.What it found: 214 colors in 38 files
#047B5D in OrderBadge.css, line 12, becomes --color-static-bg-success-filled.#E8F5EE in Toast.css, line 6, could be --color-static-bg-success-default or --color-static-bg-brand-subtle.#FF5A36 in PromoBanner.css, line 3. Closest: --color-static-bg-danger-filled, but it is noticeably different.brand-green in Header.css, line 4.To turn it on, a developer connects it to the app's build.
Look at the most common matches in each group. If something flagged is not a color at all, tell the agent not to flag it.
Example:
--color-static-bg-success-filled#047B5DIt should come back with something like this:
I wrote two test lines with the name and the hex code you gave me, in a scratch file outside the app, and ran the checker on them.
Line 1: background: var(--color-static-bg-success-filled);
Passed, no problems.
Line 2: background: #047B5D;
1 problem: #047B5D is a color typed as a code. It matches one name, so it will be corrected to --color-static-bg-success-filled.
The checker works as expected.
Try it: type a line, or pick an example
An interactive box. Pick an example or type style code, and the checker below it answers for each line: passed, corrected to a name, did you mean a name, or flagged with the closest name. It checks against the six success names from article 4's token file, listed under the box.
If it flags the first line, it is too strict. If it misses the code on the second, it is too loose. Either way, ask the agent to fix it and test again.
When to hand the rulebook to a developer
Hand them your list from step 1. It is the whole brief.
What to expect from the developer
The first run tells you how big the clean-up is before you start, with an end goal of zero.
Four horizontal bars in red, one per kind of flagged color, longest first: 96 match exactly one name, 87 could be more than one name, 28 are not a color in the design system, 3 are a name not in tokens.json. A total line reads 214 across 38 files.
The first run found:
The remediation: Essentially, it is a run of the build to fix per your rules. What happens after the run is every value with a clear rule is changed to its name, and the forty or so that need a decision are listed for you to decide and sign off.
Ask the developer to run it, or give your agent one prompt:
Two large numbers with an arrow between them: 214 in red, labeled typed colors across 38 files before, and 0 in green, labeled after, the build passes.
Every one of the 214 fixes was one of these.
text-muted-foreground. Now it is the place name for muted content. Nothing on screen changes; the name says what it is for.#047B5D on an icon becomes --color-static-icon-success-default. The screen may shift a little, because the name's green is the one the family chose, not the one someone typed from memory.Three cards side by side: Rename, same thing new name; Replace, a code becomes a name; Delete, lines that no longer need to exist. Each has two sentences explaining it.
background: #DCFCE7; color: #15803D; border: 1px solid #BBF7D0; @media (prefers-color-scheme: dark) { background: #14532D; color: #4ADE80; border-color: #14532D; }
background: var(--color-static-bg-success-filled); color: var(--color-static-label-success-filled); border: 1px solid var(--color-static-border-success-default);
Two code boxes side by side. On the left, a badge's style with six color codes, each underlined in red, three of them inside a dark mode block. On the right, the same badge with three place names in green and no dark mode block.
The run writes every judgment call to a decisions file for you to review. Ask the developer to send you anything from the results that needs a design review.
This is where you can make decisions and add to the rules page, design-system.md, mentioned in step 1.
reports/changeover-decisions.md
| found | changed to | why |
|---|---|---|
#22A06B, the line on the trends chart, TrendChart.tsx | --color-static-icon-success-default | Two greens could fit. The line shows things going well, so it takes success. |
#F79009, an orange used in 5 places, such as PromoBanner.css | Not changed. Waiting for you. | No role fits. Is it a new warning role, or five one-off choices? |
bg-success/10, a see-through green behind toasts, Toast.css | --color-static-bg-success-default | Closest solid color. It reads slightly stronger than before. |
| A border 3 pixels wide on cards, Card.css | 2 pixels | The nearest border width in the system. |
| A sidebar 268 pixels wide, Sidebar.tsx | 256 pixels | The nearest width step. Check the sidebar labels still fit. |
| and 35 more | ||
A table headed with the file name reports/changeover-decisions.md, with three columns, found, changed to, and why, and five example rows: a chart green mapped to the success name, an orange left waiting for the designer, a see-through green mapped to the closest solid name, a three-pixel border changed to two, and a sidebar width changed to the nearest step. A last row reads "and 35 more".
The final check. By now you have written the rules, built the checker, tested it, run it on the app, made the decisions it could not, and changed every screen over to the names.
One step is left: look at the app with your own eyes.
The checker reads the code. It cannot see what a screen actually shows, and between the code and the screen, other programs can still change a color. Ask the developer for a preview link, a private copy of the app with the changes in it, or review if live.
The checker said zero. The first screenshot showed a bug that no check for typed codes could see.
Two drawings of the same login screen, a lock icon, a title, a line of text, and a button. On the left the button has white words and no fill, so it is invisible, with a red note pointing at the empty space. On the right the button has a blue fill and white words.
The button's name was --color-static-content-primary. It looks like a token. It has the right shape and the right words. It is not one: there is no primary under static content in the token file. The browser found nothing by that name and painted nothing. The first version of the checker passed it, because it only looked for typed codes. This screen is why the second flag in lesson 10's level 1 list, a name that is not in the token file, exists.
Congratulations. A design system is now in place 👏
Find, fix, look. Write the list of what the checker flags, build it with an agent or hand it to a developer, and test it before you trust the count. Let the run fix every color with a clear rule, and decide the rest yourself. The code is the developer's; the decisions are yours.
Next · Stage 6 · Take it into Figma: the token file synced back into the design file, so the design and the code read from the same place, and the components built on the names.
The design system series