Published on May 16, 2024
Good design is essential for a successful enterprise. According to research, design-driven companies drive 32% more revenue growth over five years than those that don’t prioritize design.
And yet in some organizations, common scenarios like needing to update the primary color across all websites and applications can take nearly a month. Or, multiple teams in silos might create similar components with small but noticeable differences, leading to large overall inconsistencies. Worse still, a significant number of organizations are not yet prepared for the transformative impact AI will have on design.
The foundation of preparedness starts with design tokens — a critical element of every successful design system. Just as applications and websites utilize platforms like Contentful to manage and store data and content, design tokens function as a centralized repository where design decisions are curated and disseminated across various platforms.
This system extends far beyond merely supporting your web application; it acts as the database that directly binds information to your design files, native mobile applications, design system documentation, and now even your content platform.
In this article, I'll guide you through the process of building a basic design token system focused on colors, starting from primitive tokens — simple data representations — and advancing to more sophisticated, highly functional semantic tokens.
Contentful Studio, our solution for visually assembling pages, incorporates a token system that can be another endpoint for your design tokens. Learn more.
Design tokens are vital for capturing all the design decisions utilized within your design system. These decisions cover a variety of elements that define your product and brand, such as colors, text, borders, and animations.
Typically stored in JSON files due to their flexibility, these tokens can be transformed and integrated across various platforms through a multitude of existing transformation packages. With a robust foundation of reusable tokens encapsulated in JSON files, the entire design system is constructed.
As we build a basic token system, let's start with some fundamental definitions. Design tokens can generally be categorized into three types: primitive, semantic, and component tokens.
Primitive Tokens: These are the most basic form of tokens, reducing the infinite possibilities to a select few that are most relevant to the brand. This could range from a couple of dozen to a couple of hundred. The goal is to create a robust palette that resonates with your brand identity.
Semantic Tokens: These tokens are “semantic” in that they carry meaning and imply how and where they should be applied. They typically reference only the primitive tokens but include guidance on how colors should be used in text, the types of text to use, etc., embedding both meaning and guidance within.
Component Tokens: These tokens are specific to individual components and generally refer to semantic tokens. For example, a token defining the corner radius of a button applies exclusively to that button. Component tokens are valuable for theming scenarios where there is a need to alter not only primary colors but component-specific attributes that really empower themes to capture their unique look and feel.
Let's begin by defining our first primitive token: your primary brand color. Selecting this color likely involved extensive discussions, considerations of contrast, and compliance with accessibility standards such as the WCAG, so it’s important that we capture and store this value in a token. Starting with the basics, we will capture the hexadecimal value of this primary color and establish terminology and methods for visually expressing these concepts.
Let's take a look at our first design token:
We made a few important decisions while creating our design token. First, we prefixed the name with “color” to categorize it and distinguish it from other types of design decisions. This also helps maintain a clear structure as we expand our token system.
Next, we put this decision into our initial JSON representation, capturing not only the category (“color”) but also specifying its type. By including the type, we indicate that this token represents a color, providing guidance on how it should be processed. This clarity ensures that when the token is used across different platforms, the system knows how to adapt it for various applications, like changing it into the formats required by different technologies.
With a single design token in hand, it's a good opportunity to consider how we'll transform and distribute it to ensure it reaches all the necessary destinations. Let's explore a few fundamental, yet crucial, outputs to demonstrate how various roles within our team can utilize this token.
Here, I transformed the information in the token's JSON file into various formats: CSS using CSS properties or variables, an object for our iOS application, and synchronized it with our Figma variables. Although there are many other ways to export and utilize this token (for example, Tailwind CSS and Sass), this approach represents the basics, providing a simple structure.
In all these transformations, I aimed to maintain consistent naming conventions across different exports. This consistency ensures that everyone, regardless of role or platform, can use the same Design Language and easily find what they need in each context.
These transformations are typically done with specific packages like Style Dictionary and Knapsack. While it might seem easy to create your own transformations, these packages help keep things abstract and manageable. This abstraction becomes increasingly useful as your tokens become more complex.
We've established our primary design token and created a simple way to export it into usable code for our various roles. Now, let's build out our color palette. The primary color alone isn't enough; we need slightly darker and lighter variations, along with neutral colors like grays, to reduce the millions of possible color choices into a manageable set.
To create a more comprehensive palette, we can designate our primary color as “Primary 500,” allowing us to define lighter and darker versions. The goal is to establish a minimal but robust set of colors. When expanding the palette, consider accessibility guidelines such as WCAG contrast concerns. For example, you might ensure that colors labeled “500” and below are suitable for use on white backgrounds, while “600” and above for black backgrounds. Depending on where your brand’s existing colors are now, you may have to experiment with where and how to apply this convention.
Although it's not critical to define every rule at this stage, it's important to be aware of these considerations and build with accessibility in mind. Fortunately, these rules can be embedded in what are called semantic tokens which we will be building next, providing guidance to users so they don't need to remember which steps work in which scenarios.
With these considerations in mind, let's build out a fuller palette.
To set up a basic token system, I've added a secondary color, included stepping levels for each color, and added neutral colors (avoiding the term “gray” to keep it abstract). Additionally, I've integrated some primitive colors designed to provide feedback or indicate certain states to the user. Viewing these together helps identify any potential conflicts between brand colors and these “stoplight” colors, which are commonly used to convey meaning (like red for errors or green for success).
To manage variations, I'm using a 100-based stepping system and working to keep relative variations between the different colors as similar as possible. This allows users to easily intuit relative differences as well as allow room for subtle differences between levels. For instance, it’s common to want slight variations in light backgrounds, so adding 950 and 925 will be immediately understood.
We now have a versatile array of colors at our disposal thanks to our primitive tokens. Next, let's construct a semantic layer with these tokens, which will provide clear guidance on how to apply these colors effectively, creating a meaningful set of design rules. First, let's examine where relying solely on primitive tokens falls short.
Many design systems stop at this point, achieving a basic level of consistency, which is certainly better than having no defined palette. However, there's still a lot of ambiguity.
There are a lot of design decisions that are still going to be decided socially. For example, if you've decided that the 500-step color should be accessibility compliant for use on a white background, that information isn't inherent in the token itself — it has to be communicated separately.
Moreover, with five colors that can be used on white, designers might use different ones for text, leading to inconsistency. For instance, here is the same component designed in three different ways that would all be mostly acceptable with a primitive only system.
These small differences become really pronounced at scale, so resolving these ambiguities is important. This is where semantic tokens come in. Semantic tokens can refer to other tokens and encapsulate specific uses or guidelines. This capability allows us to take our existing palette and define specific usage patterns, reducing ambiguity and formally capturing more design decisions.
Let's look at how we might define a semantic token for our default text style and how it would map to the palette.
In our system, we prefer not to use black as the default text color. Instead, we opt for an off-gray or gray 200, which should be the primary text color in most cases. There are very few situations where this rule should not be followed, ensuring a consistent design across all pages and applications. To enforce this, we created a new semantic token called “text-default,” with gray 200 as its value. This new token conveys more information than a primitive token — it defines the role and proper application of the color.
With semantic tokens, we can now encapsulate design decisions within the system, providing clear guidance to those using the design system. Primitive tokens do not have this contextual information. By defining tokens like “text-default,” we start to build an API for our design system — a framework through which people can interact with our model.
This approach provides an additional layer of guidance, reducing ambiguity and helping people make consistent decisions without having to repeatedly consider color contrast or wonder what others are using. By embedding these design rules within the system, we create a reliable and straightforward way to maintain consistency.
Now that we've introduced a new type of token, let's take a step back to examine our distribution process and determine how to integrate this new semantic token into our existing workflow.
Looking at the code, you can see that in the tokens and in each output format, there's a way for one token to reference another or for one CSS variable to reference another. This is called an alias in design tokens. In Figma, for example, if you see a gray box around a variable, it indicates that it refers to another token within the design system. This functionality is powerful because it allows you to establish relationships between tokens and create more meaningful connections.
This capability enables you to express design guidelines in a more conversational way, guiding developers and designers on how to use primitive tokens. Instead of explaining which specific primitive token to use for a text color, you can reference a higher-level token, like "text-default," that refers to the correct primitive token. This abstraction layer provides flexibility, allowing you to update the underlying values without changing the semantic structure.
Similarly, when a designer or developer needs to find the correct color for text, they can look through the available tokens, and the answer will be clear — use the default text color which refers to gray 200. This approach simplifies the design process and ensures that everyone is on the same page, even as the underlying tokens change over time to improve accessibility, branding, or other considerations.
It's clear that we'll need more than just a default text color token, and this is where design systems can get complicated. If we limit the options for designers and developers too much, they may feel restricted, making it harder to create unique expressions that fit within the brand.
On the other hand, a design system that's too expansive can become unwieldy, making it difficult to train people and potentially leading to the opposite of your intended outcome — introducing ambiguity and complexity that causes inconsistencies.
Striking the right balance is a critical reason for creating your own design system. It allows you to establish a unique understanding and approach to expressing your brand while maintaining consistency. Given this, let's consider some common high-level categories within design systems and assess whether we should incorporate them into our semantic token system.
Looking at this list, you can probably guess where most of these variations are used. The interactive variations apply to what happens when you hover over buttons and when you press them. You have brand colors, feedback colors, and a need for variety on the page to add emphasis, allowing you to navigate around your default color without introducing excessive variability.
The graph also implies that tokens combine to form names like “text-hover” and “text-subtle.” While these names are straightforward, they can easily lead to a multi-dimensional problem resulting in an overwhelming number of tokens. For example, you might have a “subtle secondary background hover color,” resulting in hundreds of tokens if this grid is taken at face value. This is why design systems are about defining things just as much as they are about not defining things.
Building upon the concept of focusing on what is meaningful while explicitly ignoring what is not, you can design a simple two-dimensional grid for organizing design tokens as a starting point to model your tokens. The main structure of this grid involves placing the simpler, more straightforward tokens (such as basic states or properties) along the x-axis, and the more complex tokens (those with multiple variations) along the y-axis. Later when you resolve this structure further you can start outlining in more detail how this naming convention ultimately works.
In this example, a default background color token will have variations such as hover and active states, but a disabled state would not have additional states like disabled hover; this conceptualization helps to distinguish tokens that can evolve (multi-dimensional) from those that are static or terminal (end points).
Consider the token for a warning color. If you decide to further simplify your system and conclude that there aren’t necessary distinctions like subtle or minimal variations for this warning color, you could then place it directly along the x-axis. The x-axis placement would emphasize its role as a straightforward, singular design element without additional complexities.
This grid approach allows for clear visualization of how tokens interrelate and where simplifications can be made, aiding designers in making informed decisions about which aspects of the design system to elaborate on and which to streamline.
An additional benefit of this approach is that we can summarize it on a single page, making it easy for users to understand. Users can quickly scan for the role they need, look at the corresponding axis, and find the token they're looking for, significantly reducing the learning curve and minimizing the need for overly complex naming systems. The structure is simple: start with the role, find the relevant position on the x-axis, and then locate the y-axis value.
If your system needs to grow beyond this, then you may need to formalize the system further, but this is a good balance between structure and usability to grow your tokens. An example would be if you wanted to give an inverted color a hover state, you could move inverted to the y-axis.
With this simplified structure, let's go back to our basic web page example. We'll add a link and begin populating the page with content based on these semantic tokens to illustrate how they can be applied to build a consistent design system. In a real scenario, you would be doing this with a wide array of existing components or building out a suite of basic components for the first time.
I've kept the primitive tokens as reference points to clarify what's happening, but the beauty of the semantic token system is that the person applying these tokens to a page doesn't need to think about the primitives anymore. They have a clear understanding of what they need — text, background colors, and various link states — and the semantic grid provides straightforward answers for which tokens to use and their names.
We've reached an interesting juncture with our token system structured into a semantic layer. It can be used to help both designers and developers in making informed decisions and enhancing their communication, but now it can also be used to help content creators with visual web page editors.
Contentful Studio, our solution for visually assembling pages, incorporates a token system that can be another endpoint for our design tokens. It is structured to guide the design properties such as text colors and background colors, aligning closely with what occurs at our semantic layer. While some aspects like interactive states may not be included here, this demonstrates the level at which people naturally prefer to think when designing components.
Here is how it would map:
Now that we have established a simple system of primitives and semantic tokens, let's explore our first component token. Component tokens serve several purposes. They can act as a form of documentation between designers and developers, clarifying and coordinating the parts of a component that need to be developed. Additionally, they can be crucial for creating a multi-themed token system, allowing, for example, a single button in code that can be reused for different, unique brands.
However, component tokens introduce another level of abstraction that requires careful management and can become complex to maintain. For example, a design system with a single theme might be more manageable if it only utilizes semantic tokens. However, in our scenario, we are going to introduce two distinct brands so we can create some component tokens. Let's examine how we can create a button that works for both Brand A and Brand B.
To start, each brand has its own suite of tokens, a brand theme. These brand themes follow the same structural template, but with distinct values to capture each brand's unique style. For instance, in the case of our button component, we've managed to encapsulate what each brand desires, such as different primary colors and text labels. However, there's an issue with the corner radius — while Brand A prefers a medium radius, Brand B wants a full pill-shaped radius for their button component.
We have a few options moving forward. One approach could be to design the button with a variant that includes a full radius, allowing for flexibility between the two styles. However, if both brands rarely use these variations, it might lead to confusion and difficulty for designers and developers in implementing the correct style. It would become a social rule for Brand B to use the pill-shaped variation which introduces an unnecessary ambiguity.
Instead, we can address this challenge by introducing a component token. Let's explore how this would work for our button component:
We've introduced an additional layer atop the semantic tokens called the component token, specifically for the button radius. This allows each brand to customize the radius value to suit their specific style without breaking the structure of the overall tokens. As a result, designers and developers for each brand can use the default button implementation while still achieving the desired stylistic expression unique to each brand.
As with semantic tokens, you can be selective about how many component tokens you create. By maintaining a simpler system and avoiding the conversion of certain attributes — like background color — into component tokens, you emphasize the importance of using the primary brand color as the background color of the button. Design systems are instrumental not only in capturing but also in articulating strategic design decisions and preferences. Leveraging this for both consistency and simplicity is crucial.
Having established a color system that defines not only which colors to use but also how to use them across brand themes, we should clarify some of the terms used to organize tokens. For instance, the word “theme” could apply in numerous, contradictory ways. I'll use the data model from Tokens Studio and now Figma to define themes, modes, and token collections more clearly.
Mode: A mode represents variations in a token's values that apply across multiple tokens. For instance, a single token can have multiple values under different modes, allowing for adaptive designs across different environments or preferences, such as light and dark modes.
Token Collection: A token collection groups together tokens based on how modes are applied. For example, a dark and light theme might only pertain to color tokens. In this case, you would assemble a collection comprising only color tokens, which are isolated from typography and spacing.
Theme: In this context, a theme encompasses the entire set of token collections. Creating a new, different theme would involve duplicating all existing tokens and collections but with altered values that can be uniformly applied across a suite of components. This approach allows for comprehensive and cohesive theme variations across an entire application. “Theme” is sometimes used to refer to a “mode,” so it’s important to qualify what you mean by “theme.”
A mode is a variation of a token’s value. A simple example is light and dark modes:
In this example, let's create a button with a background that changes based on the theme's mode. The background should be primary 400
when presented in light mode, but in dark mode, it should switch to something darker, like primary 100
, allowing the text to stand out. Although the semantic token name remains constant (background-primary
), the value it references changes based on the mode. This concept is known as a “mode” — a single token representing different values depending on the context.
Let's see how this concept is implemented in Figma and in code.
You can see that Figma variables now have this additional dimension — a variable doesn't just have a single value; it can have a value for light mode and another for dark mode. When we transform this into CSS, we can use this contextual awareness to define our styling logic.
In CSS, the light value can be set as the default within the root scope, allowing background-primary
to refer to primary 400
. To introduce support for dark mode, we can create an additional class that changes the reference to primary 100
whenever this class is applied. This approach lets you switch between light and dark themes by adjusting the class applied to a parent element.
Modes are a powerful feature of tokens, but they aren't universal. For example, you wouldn't typically change typography based on dark mode — it doesn't relate to visual context in the same way color does. Modes apply to subsets of tokens called collections. In Figma, these are known as variable collections, representing groups of tokens with specific modes attached to them. Each variable within that collection will share the same modes.
This leads to the practice of creating different collections within Figma, each focusing on a distinct aspect of the design system. For instance, color collections can contain different modes, such as light and dark themes, while typography collections may not require modes but could vary based on breakpoints or other design criteria. Let’s add this to our system:
Modes represent variations within collections or subsets of your total tokens. So what is a theme? In this context, a theme is a complete duplication of all tokens into a separate set that can have different values. Themes are useful when you need a distinct style or appearance, such as different brand identities, while maintaining the same structural hierarchy of tokens. For example, if you're creating a single suite of design system components for two distinct brands, you might need entirely different values for tokens, but the underlying structure remains consistent. Currently, Figma does not natively support this level of theme duplication. However, Tokens Studio allows you to manage themes and even switch between them within Figma.
This flexibility can be valuable for designers and developers who work with multiple brands or need to create highly adaptable design systems. Imagine making one button in code that each of your brands can drop into their page by simply changing the values within their own brand token sheet. This is where the power of component tokens comes into play.
Now that we've defined tokens, modes, collections, and themes, let's explore how these concepts can be structured. The W3C standard for tokens doesn't explicitly recognize collections, modes, or themes, so these concepts are generally conventions derived from file organization and token structure.
Here is how this data model can be visualized:
Here is what it looks like in pseudo code:
Let's revisit our component to demonstrate how the semantic system compares to both a hardcoded system and a primitive-only token system. Imagine engaging in a conversation about each of these three approaches, and you'll start to appreciate the implications of each system.
With the hardcoded version, you might find yourself needing to write things down — there's a lot to keep track of. The primitive token system is an improvement, allowing for more abstract discussions, though there's still a significant amount of ambiguity.
For instance, when communicating specific numeric values, they might not hold much inherent meaning. However, the semantic system feels intuitive and conversational. You might say, "Let's use the default color, go with the subdued for the header to make it stand out, and just use regular padding here."
This approach not only feels natural, but it also simplifies the communication process, making it feel like you're having a genuine conversation that can also be easily validated as correct in it’s code implementation.
After walking through the process of building our basic token system into semantic tokens and primitives and adding modes, we've developed a structured grid that can be easily referenced and used by your team. Alongside this, we've designed a basic code architecture that can be captured in JSON, transformed using tools like Style Dictionary, and exported to formats like CSS and Swift, with synchronization in Figma.
Although the basic token processing might seem simple, you can start to understand the benefits of using a transformer in the middle. These tools automatically handle complex tasks like resolving aliases and offer functionality like changing modes by creating CSS classes. This automation helps maintain a consistent design system without requiring manual adjustments or complex coding.
Here's a final visualization of this architecture but emphasizing the hub and spoke model:
The tokens sit in the center of this architecture, serving as the foundational data. The transformer wraps around them, generating various outputs like XML, Swift, and CSS, and synchronizes the Figma variables. This structure allows the design system to produce multiple formats from a single source, ensuring consistency across different platforms and applications.
One caveat with this architecture is that Figma can potentially push changes back into the token set, even considered the source of truth if the core token JSON is not to be edited. This can be a valuable approach if you want to empower designers to make changes directly and define the actual choices for tokens. It allows for more flexibility and a design-driven process, and can be accomplished via Figma’s REST or Plugin APIs.
Despite over a decade of discussion around design tokens, it hasn’t felt like they have had the same momentum as other powerful technologies like JavaScript frameworks or UX design tooling like Figma.
The challenge has largely stemmed from difficulties in seeing and capturing the value from establishing what could be a complex system that spans both designers and developers. Even the token standard struggles in its clunkiness and failure to capture essential data structures without relying on idiosyncratic conventions.
Previously, it might have been viable to avoid creating design tokens, but two key developments have significantly altered this landscape: Advancements in design tools, and artificial intelligence in design systems.
Tools like Figma and Tokens Studio have revolutionized our capacity to implement robust, workable token systems within design tools that are deeply tied to your source code. They are up to the task of being source files. While still unfolding, the pace of improvement is rapid and substantial (and not in the “AI will be driving your car” sense), and will eventually pull the W3C forward and the related tooling.
Design systems, inherently repetitive, aim to make designs predictable and clarify ambiguities—a perfect match for AI. AI is poised to transform how design systems are built, moving from large teams with extensive experience to smaller more streamlined teams that can deliver bespoke, robust design systems, and components rapidly.
However, AI will not magically correct deficiencies within a design system. It is making inferences from large data sets, meaning it will amplify and extend the foundational structures provided and make up what’s missing. With a solid base of design tokens and a systematic approach to applying them, asking AI to scale out your design system will not only work, but will be what you will be competing against soon.
Subscribe for updates
Build better digital experiences with Contentful updates direct to your inbox.