Design_systems_for_developers_Learn_how_to_code_design_systems_that
Design_systems_for_developers_Learn_how_to_code_design_systems_that
Developers
Learn how to code design systems that
scale
Michael Mangialardi
This book is for sale at
http://leanpub.com/designsystemsfordevelopers
1 - The Mission . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 - The Problem . . . . . . . . . . . . . . . . . . . . . . . . . . 4
12 - The Conclusion . . . . . . . . . . . . . . . . . . . . . . . 60
1 - The Mission
Ok, so you’re a really passionate UI developer…
Great! We’ve got an exciting project that we want to entrust with
you.
Our top-notch, e-commerce site ZapShoes provides the zappiest
way to get shoes delivered to your doorsteps.
We recently raised 100 million dollars in funding, and we believe we
can keep the momentum going by giving our application a facelift.
We’ve hired a core group of designers that have whipped up a fresh
design system.
So far, the design system just includes a set of Figma files.
Since you’re the UI guru, we want you to build tools that will ensure
that our current web application can successfully be migrated to the
new design system and stay in sync with it.
We also want the tooling to be flexible to be able to easily handle a
future redesign.
Moreover, we want to ensure that if we add applications for other
platforms, like mobile and desktop, that we have a smooth way of
ensuring that those applications can adopt and stay in sync with
the design system.
Imagine being a new hire and assigned the project as I’ve outlined
above.
How would you feel?
Well, given that you’re a developer, you may feel excited.
1 - The Mission 2
After all, developers love solving problems. At the end of the day,
the project is just another problem to solve, and solving problems
is fun.
You might also feel energetic.
A design system is a really fun thing to work on.
It’s a perfect blend of doing something that’s highly visual and
architectural, requiring you to think on a large scale of how systems
work together.
It satisfies both the visually orientated and engineering-driven sides
of a frontend engineer, a combination that is hard to come across.
More likely than not, you would feel nervous.
Creating the tooling around a design system is an exciting, energiz-
ing task, but it is also intimidating.
There is a whole lot of ground to cover, starting from receiving a set
of design files to delivering assets to an indefinite list of consuming
applications, regardless of platform and technology.
You are molding the way for the future yet working within the
bounds of current technology, which is always subject to change.
There is an indefinite list of stakeholders interested in the tooling
around the design system, each wanting the tooling to work well
for their use cases.
Let me encourage by making some remarks which hopefully will
take the bite out of these intimidating thoughts.
First, if you are a developer you love to solve problems. Not only
that, I completely trust that as a developer, you are talented in being
able to solve problems. A design system is just another problem that
you are capable of solving.
Second, a design system is satisfying to work on as mastering it
will give you an appealing professional skill, enlarging your critical
thinking, communication, and coding skills.
1 - The Mission 3
By the end of it, we will know how to solve the imaginary project
that ZapShoes has assigned to us. In other words, you’ll be able to
develop tools for a robust design system all by yourself.
2 - The Problem
There are several difficulties in trying to develop tools for a design
system.
First, the design system has to scale as more and more applications
are added within a company.
Second, the design system has to be consumable across various
types of platforms (i.e. web, mobile, desktop) as well as specific
technologies for those platforms (i.e. React, Vue, Angular).
Third, the design system has to be flexible to handle updates by the
designers.
Similarly, and finally, the design system as implemented by con-
suming applications has to be high fidelity. Meaning, it has to be
pure, or faithful, to the official specifications.
Let’s talk briefly about some real-life scenarios where these pitfalls
are encountered.
The first couple of difficulties are usually encountered together.
Imagine that your company, ZapShoes, only consisted of an e-com-
merce site built with React. To accomplish the project we outlined
in the previous chapter, you decided to build a UI component
library written in React. That way, you can encapsulate all the
design specifications within sharable components.
Now, that only works as long as ZapShoes only consists of a React
app. However, another team is spun up to create a customer support
app. They decide to build this with Vue (despite your best efforts to
persuade them otherwise).
Because of this, you can no longer apply the styles from the design
system by consuming a shared UI component library. The React
app will need a UI component library that works with React, and
2 - The Problem 5
the Vue app will need a UI component library that works with Vue.
The design specifications are then encapsulated in a shared CSS-in-
JS library. This is used to get the design specifications for the UI
components in both the React and Vue UI component libraries.
That works as long as all the applications at ZapShoes are built
with modern web frameworks. However, there’s been a push to “go
back to the basics” with a marketing landing page that’s built with
plain HTML and CSS. Now, the design specifications need to be
encapsulated into a CSS file as that is the only common way to
share styles across all three applications in the company.
This stinks because you either need to build some tools to port the
CSS to the CSS-in-JS library, or you continue to use plain CSS which
doesn’t give you the power of CSS-in-JS which you prefer.
You can imagine how this problem can spiral downwards if a
mobile application built with Java was announced.
The last couple of difficulties manifest themselves when things do
spiral downwards as in the example I just provided. Imagine that
because of the sticky situation to either use plain CSS or make a
tool to port CSS to a CSS-in-JS library, you decide to have the
design specifications encapsulated in the CSS file and a JavaScript
file referenced in the CSS-in-JS library.
That seems to make everyone happy as the plain HTML coders can
create CSS variables and classes to match the design specifications
from the design system, and the modern JavaScript coders can cre-
ate JavaScript objects or modules to match the design specifications
from the design system.
The problem is that you now have two places where the design
specifications of the design system are trying to be represented in
code. One in CSS variables and classes, and another in JavaScript
objects/modules. Imagine that updates are made to the design
system. The modern JavaScript coders update the JavaScript ob-
jects/modules referenced in the shared CSS-in-JS library so that it
is in sync with the latest version of the design system. However,
2 - The Problem 6
the plain HTML coders are lagging behind (…imagine that…) and
haven’t updated the CSS variables and classes to match the latest
version of the design system.
In this scenario, where is the “pure,” or “high fidelity,” representa-
tion of the design specifications in code? Is it the CSS variables and
classes or the JavaScript objects/modules? As time goes on, it only
becomes harder and harder to answer this question.
Not only is it hard to identify where the pure representation
of the design specifications in code lives, but, practically, your
applications can begin to look different depending on how closely
they sync up with changes to the design system. Additionally, there
is an increased risk that the the official design specifications get
lost in translations as more and more applications maintain their
separate representations of design specifications in code.
These problems can be solved by using design tokens and a style
dictionary. We’ll unpack those terms more in the following chap-
ters.
3 - Introducing Design
Tokens
Every design system has the “high fidelity” design specifications
encapsulated in a design file, and usually some form of docu-
mentation. These design files are called “high fidelity” because
they are the purest, or most faithful, expression of the design
system. In other words, if you see a design specification in the
design file, it is highly likely that it is what the designers want
a consuming application to emulate. If the design file says to use
#ff000 as the color for error text, then, as a developer, you trust
that is what your application should follow. The only way that the
specification is “unpure,” or inaccurate of the designers’ intent, is if
someone made a mistake when creating the design file and didn’t
realize it. Regardless if there are actually any mistakes, developers
always go off of what the latest version of a design file (or some
documentation) specifies.
Whether it’s in CSS variables, SASS variables, JavaScript objects,
or something else, all the assets/tools of a design system have to
translate the official design specifications from a design file. The
high fidelity design specifications in the design file have to be
encapsulated, or represented, in some code. The goal is to have
design specifications represented in code to be high fidelity as well,
in that it follows the design file with 100% accuracy.
The point of this all is to say that the design specifications have
to be represented in code and that representation is important. So
important that it deserves a name. Design tokens are simply a name,
or label, for the design specifications you represent in code.
Now, a design file has different types of design specifications.
There are design specifications that simple values. They are simply
3 - Introducing Design Tokens 8
to their needs.
So, why is this important?
First, it becomes very easy to keep all the applications that consume
a design system in sync with the latest, official specifications of the
design system. The reason being, you only have a single place to
keep in sync. Instead of all the consuming applications maintaining
their own set of design tokens, you move the maintenance to a
single location.
Second, by having only a single place to store the design tokens,
designers and developers only have to glance at this central place
to determine what design specifications are (or are not) being
implemented across all applications within a company. You no
longer have to check all the CSS and/or JavaScript files across every
shared library and/or consuming application to determine whether
things are in sync with the official design specifications.
Finally, building platform and technology-specific deliverables
from the platform and technology-agnostic design tokens allow
for a design system to scale well within a company. Even if more
applications are added and/or some applications target a new
platform and tech stack, you would just deliver the platform
deliverables that are being built or configure new platform
deliverables to be built and delivered. Put it this way, since
you start with the most agnostic representation of your design
specifications, that is your design tokens, you can just generate
more platform and technology-specific deliverables as needed.
By doing this, you avoid the problem of making a platform and
technology-specific encapsulation of your design specifications
(like a file in a CSS-in-JS library) and having to work backward
as new platforms and technologies are supported. You don’t have
to be pigeonholed to the most agnostic asset to encapsulate your
design specifications (like a CSS file) or support a wide range of
assets without having a single source of truth.
Just as we had a label for the platform and technology-agnostic
4 - Introducing Style Dictionaries 11
communication.
Before we move on, I’d like to clarify that I am not saying that
you should avoid an automated approach, nor am I suggesting that
communication between designers and developers can only occur
with a manual approach. My point is that the manual approach is
not bad and is good for learning purposes. If you do go with the
automated approach, my suggestion would be to go all-in with it.
For example, I would recommend the Specify⁶ app which allows
you to manage, transform, and deliver design tokens with just a
Figma file, Figma API token, and a configuration file. Instead of
stitching together existing tools for this automation, you can do
it through a UI, making it a bit less intimidating for designers.
The creator, Louis Chenais⁷, is a part of the design tokens W3C
Community Group, so you can trust that the tooling will stay in
sync with the community. I have chatted with Louis and done
a demo of the app. You can do so as well using the link found
above. However, this app is only accessible by invite. You will have
to weigh the tradeoffs to decide if this tool is right for you. Just
remember, you’re currently employed at ZapShoes so I recommend
deferring looking into this until after you finish the book.
Eventually, we will discuss some formal processes for collaborating
with designers and developers to create design tokens from design
files. In the next chapter, we will look at how to come up with names
for your design tokens.
⁶https://specifyapp.com/
⁷https://twitter.com/ChucKN0risK
7 - Valuing Design Tokens
Congratulations! If you’ve made it this far, you’ve gotten through
the hardest part. This is also the last abstract chapter before we
finally get to code. We’ll keep this chapter short than the previous
one, discussing some considerations for how we represent the
values of our design tokens.
First, there are some things to consider when representing the value
for color tokens. Colors can be represented as Hex, RBG, or HSL.
Some prefer to go with HSL because it is easier to parse the color
from the value as well as providing more programmatic control for
creating different shades of the same pigment. You can read this
article for more details⁸.
My suggestion is that you use whatever is consistent with the
design files. If the designers prefer RGB, it wouldn’t be a great idea
to introduce a discrepancy between the design files and the style
dictionary. After all, the style dictionary is supposed to be a “source
of truth” to make it easier (not harder) to see how well the design
specifications are represented in code. With Style Dictionary, you
can easily transform any color format found in the JSON objects to
another one according to the preferences of the developers. So, you
could convert HEX colors in your design tokens to HSL colors in
your platform deliverables.
Similarly, other design tokens have a value that will be a number.
For example, the line height, spacing, font size, border radius, etc.
have numbers for the values (whereas a font, for comparison, has
a value of a string).
There is a case to be made that a line height should always be
defined as a unitless value⁹. As for scalable elements, such as
⁸https://www.sarasoueidan.com/blog/hex-rgb-to-hsl/
⁹https://css-tricks.com/almanac/properties/l/line-height/
7 - Valuing Design Tokens 18
spacing, font size, etc., there is a case to be made that rem should be
the unit of choice¹⁰. Regarding non-scalable elements, such as the
border radius, border width, box shadow, etc., there is a case to be
made to use pixels, a static unit, as opposed to a relative unit.
You’ll have to do your research on these topics. All you need to
know, for now, is that you should expect these types of conversa-
tions with the design system team as you are trying to translate
design files into design tokens.
¹⁰https://css-tricks.com/theres-more-to-the-css-rem-unit-than-font-sizing/
6 - Naming Design Tokens
Now that we’ve established that designers and developers will
collaborate to manually store design specifications as design tokens,
let’s talk about how to turn a design specification into a design
token, that is, how to turn a design specification into a key-value
pair that will be part of a JSON object.
This is easy to speak of abstractly, but it is not a simple task. There
is a lot to consider when creating both a key and a value. Because
of that, we’ll tackle each in a separate chapter. For now, let’s focus
on creating a key, or naming our design tokens.
You’ll remember that design tokens may be categorized into two
types: simple and composed (also called component) tokens.
Simple tokens are design tokens that represent the enumeration of
valid values of a design system, such as color palette, typography,
etc.
Composed tokens are compositions of simple tokens specifying how
a value should be used in a specific context.
For example, monday.com UI Kit¹¹ has the following color palette
specified in a Figma file:
¹¹https://www.figma.com/community/file/940242815162888749
6 - Naming Design Tokens 20
This represents the list of valid colors of the design system. Simple
tokens are pretty easy to come up with when looking at the design
file.
For example, here are some design tokens you could create:
1 {
2 "color.primary": "...",
3 "color.success": "...",
4 "color.dark: "...",
5 "color.darkRed: "..."
6 }
Now, do you prepend the color type with color or colors for
example? Do you use kebab-case or snake_case?
Style Dictionary will not be impacted by these decisions. It comes
down to what the designers and developers think best. No team
is the same, so y’all will have to hash it out together. You’ll just
want to consider that clarity and brevity are what you’re looking
for. At the end of the day, it is the developers who will be consuming
these design tokens via the platform deliverables. As long as it is
clear enough what a design token is referring to, make it as brief as
possible to the developers’ liking.
6 - Naming Design Tokens 21
1 {
2 "border.radius.sm": "...",
3 "border.radius.md": "...",
4 "border.radius.lg": "..."
5 }
However, that’s not always the case. For example, you may want to
combine margin and padding specifications into a generic spacing
prefix:
1 {
2 "spacing.sm": "...",
3 "spacing.md": "...",
4 "spacing.lg": "..."
5 }
Second, the order is not an exact science, but it does matter. Nathan
Curtis suggests that namespace levels come first, then object, levels,
then base levels, and then the modifier levels come at the end.
Before moving on to the next chapter, it could be helpful to look
over Nathan Curtis’s article. Browse an existing design system¹³
and think about how you would come up with names for design
tokens given the examples and principles in the article. However,
don’t get discouraged if this is a lot to think about. Remember,
you are not alone. The whole process in a real-world scenario
will be a collaboration between designers and developers. To be
a successful developer working on a design system team, you just
need to understand the problem, have an overview of the solution,
and initiate conversation.
¹³https://www.designsystemsforfigma.com/
8 - Storing and
Transforming Design
Tokens
Now that we have an idea of how to extract design tokens from
design files, let’s go ahead and create our style dictionary.
As a refresher, the style dictionary will be an npm project that
we’ll push to a GitHub repository. The project will contain “store”
our design tokens. Specifically, we will have a JSON file with
properties (key-value pairs) on the object representing the design
specifications. The project will have Style Dictionary¹⁴, an npm
package that exposes a CLI to transform the JSON file into platform
deliverables, as a dependency.
Let’s begin by setting up the project.
Create a new folder for our project:
1 mkdir zappy_design
2 cd zappy_design
1 npm init -y
¹⁴https://github.com/amzn/style-dictionary
8 - Storing and Transforming Design Tokens 25
1 mkdir tokens
1 touch tokens/index.json
With Style Dictionary, you can divide your design tokens into
multiple JSON files. During the build process, it will merge together
all your JSON files before generating the platform deliverables.
So, you could have files like a colors.json, spacing.json, etc.
We’ll leave it at just index.json.
As we’ve talked about, extracting the names and values for design
tokens is a collaborative process. There is no way that we can do
that full process together through a book. For that reason, I’ll just
supply a handful of example tokens that you can copy into the
tokens/index.json file:
1 {
2 "color.primary": "#00C875",
3 "color.accent": "#8B46FF",
4 "color.success": "#11AF22",
5 "color.error": "#BA1E45",
6 "color.white": "#FFFFFF",
7 "color.black": "#000000",
8 "spacing.0": "0rem",
9 "spacing.1": "0.25rem",
10 "spacing.2": "0.5rem",
11 "spacing.3": "0.75rem",
12 "spacing.4": "1rem",
8 - Storing and Transforming Design Tokens 26
13 "spacing.5": "1.25rem",
14 "spacing.6": "1.5rem",
15 "spacing.7": "1.75rem",
16 "spacing.8": "2rem",
17 "line.height.none": "1",
18 "line.height.tight": "1.25",
19 "line.height.snug": "1.375",
20 "line.height.normal": "1.5",
21 "line.height.relaxed": "1.625",
22 "line.height.loose": "2",
23 "font.weight.thin": "100",
24 "font.weight.light": "300",
25 "font.weight.normal": "400",
26 "font.weight.bold": "700",
27 "font.size.1": "0.75rem",
28 "font.size.2": "0.875rem",
29 "font.size.3": "1rem",
30 "font.size.4": "1.250rem",
31 "font.size.5": "1.625rem",
32 "font.size.6": "2rem",
33 "font.family": "Open Sans, sans-serif",
34 "breakpoint.mobile": "40rem",
35 "breakpoint.tablet": "65rem",
36 "border.radius.none": "0",
37 "border.radius.small": "2px",
38 "border.radius.medium": "4px",
39 "border.radius.large": "6px",
40 "component.button.base.font.size": "0.875rem",
41 "component.button.base.font.weight": "700",
42 "component.button.base.padding": "0.75rem",
43 "component.button.primary.background.color": "#00C875",
44 "component.button.primary.color": "#FFFFFF",
45 "component.button.primary.hover.background.color": "#FF\
46 FFFF",
47 "component.button.primary.hover.color": "#00C875",
8 - Storing and Transforming Design Tokens 27
48 "component.button.secondary.background.color": "#8B46FF\
49 ",
50 "component.button.secondary.color": "#FFFFFF",
51 "component.button.secondary.hover.background.color": "#\
52 FFFFFF",
53 "component.button.secondary.hover.color": "#8B46FF"
54 }
1 touch config.json
1 {
2 "source": ["properties/**/*.json"],
3 "platforms": {
4 "scss": {
5 "transformGroup": "scss",
6 "prefix": "sd",
7 "buildPath": "build/scss/",
8 "files": [{
9 "destination": "_variables.scss",
10 "format": "scss/variables"
11 }],
12 "actions": ["copy_assets"]
13 },
14 "android": {
¹⁵https://amzn.github.io/style-dictionary/#/config
8 - Storing and Transforming Design Tokens 28
1 $color-background-base: #f0f0f0;
2 $color-background-alt: #eeeeee;
You can register custom formats¹⁹ just as you can register custom
transforms.
Finally, Style Dictionary exposes pre-defined actions²⁰ that will do
something after the platform deliverables are generated. We will
not be needing to use these.
We can also see that the sample configuration configures a platform
deliverable for the Android platform, demonstrating that you can
create deliverables across multiple platforms. In a real-world sce-
nario, you would just configure a new platform deliverable when
the need arises.
¹⁹https://amzn.github.io/style-dictionary/#/formats?id=creating-formats
²⁰https://amzn.github.io/style-dictionary/#/actions
8 - Storing and Transforming Design Tokens 30
1 {
2 "source": ["tokens/**/*.json"],
3 "platforms": {
4 "scss": {
5 "transformGroup": "scss",
6 "buildPath": "build/scss/",
7 "files": [{
8 "destination": "_variables.scss",
9 "format": "scss/variables"
10 }]
11 },
12 "javascript": {
13 "transformGroup": "js",
14 "buildPath": "build/js/",
15 "files": [{
16 "destination": "_variables.js",
17 "format": "javascript/es6"
18 }]
19 }
20 }
21 }
Here, we are telling Style Dictionary to find all JSON files in the
tokens directory. Then, we want it to create SCSS and JS variables
using the pre-defined options. The results should be written to a
build directory.
Finally, we can create an npm script that will execute the Style
Dictionary CLI, building the platform deliverables from the raw
design tokens.
8 - Storing and Transforming Design Tokens 31
1 "scripts": {
2 "build": "style-dictionary build"
3 },
I also like to clean the build folder before running the build script,
that way you don’t have old platform deliverables that stick around:
1 "scripts": {
2 "build": "npm run clean && style-dictionary build",
3 "clean": "style-dictionary clean"
4 },
What gives?
Well, I haven’t mentioned this yet in order to avoid confusion.
However, Style Dictionary does have an opinion when it comes to
a convention for storing design tokens. It expects a Category/Type-
/Item (CTI) convention²¹. It is more than one of the many naming
conventions that are out there. It is a classification structure.
1 {
2 "color": {
3 "background": {
4 "button": "#00C875"
5 }
6 }
7 }
and actions. So, this isn’t a huge deal. We’ll update our configura-
tion by registering a custom format that can handle the convention
we followed in our tokens/index.json file.
First, create a new file called config.js at the root of the project.
Style Dictionary allows you to use a JavaScript file as the configura-
tion file. This will allow us to register a custom format in addition
to exporting the settings.
Next, let’s copy the JSON configuration and translate it to JS format:
1 module.exports = {
2 source: ["tokens/**/*.json"],
3 platforms: {
4 scss: {
5 transformGroup: "scss",
6 buildPath: "build/scss/",
7 files: [
8 {
9 destination: "_variables.scss",
10 format: "scss/variables",
11 },
12 ],
13 },
14 javascript: {
15 transformGroup: "js",
16 buildPath: "build/js/",
17 files: [
18 {
19 destination: "_variables.js",
20 format: "javascript/flat",
21 },
22 ],
23 },
24 },
25 };
8 - Storing and Transforming Design Tokens 34
35 ],
36 },
37 },
38 };
1 module.exports = {
2 "color.primary": "#00C875",
3 "color.accent": "#8B46FF",
4 "color.success": "#11AF22",
5 "color.error": "#BA1E45",
6 "color.white": "#FFFFFF",
7 "color.black": "#000000",
8 "spacing.0": "0rem",
9 "spacing.1": "0.25rem",
10 "spacing.2": "0.5rem",
11 "spacing.3": "0.75rem",
12 "spacing.4": "1rem",
13 "spacing.5": "1.25rem",
14 "spacing.6": "1.5rem",
15 "spacing.7": "1.75rem",
16 "spacing.8": "2rem",
17 "line.height.none": "1",
18 "line.height.tight": "1.25",
19 "line.height.snug": "1.375",
8 - Storing and Transforming Design Tokens 37
20 "line.height.normal": "1.5",
21 "line.height.relaxed": "1.625",
22 "line.height.loose": "2",
23 "font.weight.thin": "100",
24 "font.weight.light": "300",
25 "font.weight.normal": "400",
26 "font.weight.bold": "700",
27 "font.size.1": "0.75rem",
28 "font.size.2": "0.875rem",
29 "font.size.3": "1rem",
30 "font.size.4": "1.250rem",
31 "font.size.5": "1.625rem",
32 "font.size.6": "2rem",
33 "font.family": "Open Sans, sans-serif",
34 "breakpoint.mobile": "40rem",
35 "breakpoint.tablet": "65rem",
36 "border.radius.none": "0",
37 "border.radius.small": "2px",
38 "border.radius.medium": "4px",
39 "border.radius.large": "6px",
40 "component.button.base.font.size": "0.875rem",
41 "component.button.base.font.weight": "700",
42 "component.button.base.padding": "0.75rem",
43 "component.button.primary.background.color": "#00C875",
44 "component.button.primary.color": "#FFFFFF",
45 "component.button.primary.hover.background.color": "#FF\
46 FFFF",
47 "component.button.primary.hover.color": "#00C875",
48 "component.button.secondary.background.color": "#8B46FF\
49 ",
50 "component.button.secondary.color": "#FFFFFF",
51 "component.button.secondary.hover.background.color": "#\
52 FFFFFF",
53 "component.button.secondary.hover.color": "#8B46FF"
54 }
8 - Storing and Transforming Design Tokens 38
Cool! Now, we will need to register another custom format for SCSS
variables:
34 files: [
35 {
36 destination: "_variables.scss",
37 format: "scss/flat", // variables --> flat
38 },
39 ],
40 },
41 javascript: {
42 transformGroup: "js",
43 buildPath: "build/js/",
44 files: [
45 {
46 destination: "_variables.js",
47 format: "javascript/flat",
48 },
49 ],
50 },
51 },
52 };
1 npm i -D lodash
Running npm run build again, we can see that it generates a nice
looking set of SCSS variables:
8 - Storing and Transforming Design Tokens 40
1 $color-primary: #00C875;
2 $color-accent: #8B46FF;
3 $color-success: #11AF22;
4 $color-error: #BA1E45;
5 $color-white: #FFFFFF;
6 $color-black: #000000;
7 $spacing-0: 0rem;
8 $spacing-1: 0.25rem;
9 $spacing-2: 0.5rem;
10 $spacing-3: 0.75rem;
11 $spacing-4: 1rem;
12 $spacing-5: 1.25rem;
13 $spacing-6: 1.5rem;
14 $spacing-7: 1.75rem;
15 $spacing-8: 2rem;
16 $line-height-none: 1;
17 $line-height-tight: 1.25;
18 $line-height-snug: 1.375;
19 $line-height-normal: 1.5;
20 $line-height-relaxed: 1.625;
21 $line-height-loose: 2;
22 $font-weight-thin: 100;
23 $font-weight-light: 300;
24 $font-weight-normal: 400;
25 $font-weight-bold: 700;
26 $font-size-1: 0.75rem;
27 $font-size-2: 0.875rem;
28 $font-size-3: 1rem;
29 $font-size-4: 1.250rem;
30 $font-size-5: 1.625rem;
31 $font-size-6: 2rem;
32 $font-family: Open Sans, sans-serif;
33 $breakpoint-mobile: 40rem;
34 $breakpoint-tablet: 65rem;
35 $border-radius-none: 0;
8 - Storing and Transforming Design Tokens 41
36 $border-radius-small: 2px;
37 $border-radius-medium: 4px;
38 $border-radius-large: 6px;
39 $component-button-base-font-size: 0.875rem;
40 $component-button-base-font-weight: 700;
41 $component-button-base-padding: 0.75rem;
42 $component-button-primary-background-color: #00C875;
43 $component-button-primary-color: #FFFFFF;
44 $component-button-primary-hover-background-color: #FFFFFF;
45 $component-button-primary-hover-color: #00C875;
46 $component-button-secondary-background-color: #8B46FF;
47 $component-button-secondary-color: #FFFFFF;
48 $component-button-secondary-hover-background-color: #FFFF\
49 FF;
50 $component-button-secondary-hover-color: #8B46FF
I don’t like this option because it seems to defeat the whole purpose
of having a style dictionary in the first place. Using Style Dictionary,
we can export tokens that work for Android. However, Android
coders won’t be able to consume an npm package. This solution
seems inflexible to go beyond the web platform.
Another option is to create an API. Theoretically, each consuming
application could have a script that calls the API and requests the
platform deliverables it is interested in. For example, a Node appli-
cation could have a script that requests the JavaScript module we
created in the previous chapter. The downside is that either there
has to be a process in place to communicate to all the consuming
applications that an update has occurred and new deliverables are
available; or, each application would have to do routine checks.
9 - Delivering Design Tokens 43
For example, you could set an event that when code is pushed to a
branch, it will run your unit tests and report the status.
These actions will run in a virtual environment which has to be
specified. You can learn more about the virtual environment here²⁴.
Let’s create a workflow to deliver the platform deliverables to a
consuming application when Style Dictionary generates them.
First, we’ll add a YAML file that will contain the code for our
workflow:
1 mkdir .github/workflows
2 touch .github/workflows/deliver-tokens-to-consumers.yml
Finally, we’ll give our workflow a job of checking out our code and
copying the build path to a path on another GitHub repository (the
repository of a consuming application in a company):
²⁶https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
²⁷https://docs.github.com/en/actions/reference/events-that-trigger-workflows
9 - Delivering Design Tokens 46
Each job has a series of steps. The first step is to use a pre-defined
GitHub Actions’ step. Namely, the actions/checkout²⁸ step which
checks out the code of our repository in the virtual environment.
You can see the full set of pre-defined steps here²⁹.
Then, we use a user-made step called copycat-action³⁰. This step
allows you to copy files from your repository to another repository.
We use this to copy the built JavaScript variables encapsulating our
design tokens to another repository.
That’s it!
Since each application will only be interested in receiving a specific
type of platform deliverable that was generated by Style Dictionary,
we can create a separate “job” for each application.
Jobs in GitHub Actions run in parallel by default.
So, on the push of new platform deliverables, each application that
needs to consume one of these deliverables will have a job in the
workflow to copy the needed deliverable to its repository.
This means that for every new application that consumes the style
dictionary, we would just have to add a new job in the workflow
for them to stay in sync with changes to the design tokens.
To test this out, let’s create a
dummy repository
consume-style-dictionary-github-actions. After you initialize
this new repository on GitHub, you’ll need to read the comments
in the workflow file.
You’ll need to follow the documentation³¹ to create a GitHub
Personal Access Token on the zappy-design repository. This grants
permission to the copycat-action step to copy code from the
zappy-design repository. Make sure to name it API_TOKEN_GITHUB
for the workflow code to work.
²⁸https://github.com/actions/checkout
²⁹https://github.com/actions
³⁰https://github.com/andstor/copycat-action
³¹https://docs.github.com/en/actions/reference/encrypted-secrets
9 - Delivering Design Tokens 48
You’ll also need to replace the dst_owner with the repository owner
of the dummy repository you just initialized. Most likely, this is
your GitHub username.
Finally, you may need to update the branches to main instead of
master.
³⁵https://stitches.dev/
³⁶https://github.com/michaelmang/zappy_design/blob/master/tokens/index.json
³⁷https://tailwindcss.com/docs/theme
10 - Creating Tools and Assets From Design Tokens 54
1 const _ = require("lodash");
2
3 const tokens = require("../build/js/_variables.js");
4
5 const colors = () => {
6 const colorTokens = Object.entries(tokens).filter(([key\
7 ]) =>
8 key.startsWith("color")
9 );
10 return Object.fromEntries(
11 colorTokens.map(([key, value]) => {
12 const colorName = key.replace("color.", "");
13 return [colorName, value];
14 })
15 );
16 };
17
18 const fontFamily = () => ({
19 sans: _.get(tokens, "font-family"),
20 });
21
22 const fontSize = () => {
23 return Object.fromEntries(
24 Object.entries(tokens).filter(([key]) => key.includes\
25 ("font-size"))
26 );
27 };
28
29 const screens = () => ({
30 sm: _.get(tokens, "breakpoint.mobile"),
31 md: _.get(tokens, "breakpoint.tablet"),
32 });
33
34 const spacing = () => {
35 return Object.fromEntries(
10 - Creating Tools and Assets From Design Tokens 55
could be used to generate action items for each team so that they
can more closely adopt the official design system. The process of
scorecard testing, however, assumes an inventory of stakeholders
in a company. You’ll want to be intentional about gathering this
information early on in the process.
Once a design system has been built upon a solid foundation of a
style dictionary, adopted by various applications, and tested for its
adoption success, you can begin the fun part of building tools off
of the design tokens as we discussed in the last chapter.
Again, I’m left having to say that there is no way that I can tell
every design system team that exists today, and that will exist in
the future, what to do and how exactly to do it all. However, I hope
that this provides some helpful direction.
12 - The Conclusion
Congratulations! You have made it through the book.
Let’s recap our original mission.
The original mission was to walk with you as a fellow nervous
developer to grow our ability to solve the problems surrounding
design systems.
Together, we have learned about the challenges to creating a com-
pany-wide design system. But, we discovered what design tokens
and style dictionaries are and how they may help fill in some of
the gaps. We’ve stored up principles for creating design tokens
as well as technical skills to build a style dictionary and deliver
the transformed design tokens to an infinite amount of consuming
applications. We’ve talked about the potential for building tools and
assets from the transformed design tokens to improve the developer
experience of all the developers within a company that are trying to
adopt a design system. Finally, we’ve learned about collaborating
with designers and other developers so that you don’t have to go
through this process alone.
Let me repeat the imaginary problem we were given at the start of
this book.
If you were given this problem, would you know how to solve it? I
assure you that you certainly do!
Remember, creating a robust design system is just a problem. As
a developer, you are naturally good at solving problems. If you
don’t know how to solve a problem, you know where to go looking
for answers. In this case, you do have some tangible answers and
direction for solving this particular problem.
You may not understand how to extract every design specification
from a design file, but you have a framework for thinking about it,
and you know that you’ll have help.
You may not know what custom Style Dictionary formats will be
needed to create the platform deliverables that stakeholders need,
but you know where to begin.
12 - The Conclusion 62
You may not know what tools people want built on top of the
transformed design tokens, but you are able to help when the need
arises.
You may not know what designers and developers you will be
collaborating with and what processes will come from it, but you
know how to begin the conversation and gain their trust.
You may still be at the start of a journey of working on developing
a design system, but you are further along then when you started.
It has been my pleasure to transfer my little bit of knowledge on
this subject to you. And, I hope and trust that you’ll do great things
with it.
Every blessing, Michael Mangialardi
Here is the final repository for this book⁴⁰
⁴⁰https://github.com/michaelmang/zappy_design