Evolution of design tokens and component styling, part 1

Brian Heston
FAST
Published in
9 min readAug 8, 2022

Here is a look at the state of design tokens in the industry, how they currently manifest in FAST (part 2), and how that might evolve for improved and unified support in the future (part 3 coming soon).

Design tokens in the industry

Defined generally, “design tokens” are quickly becoming the industry-wide method to organize and build design values into interface components. Unfortunately, there are currently many implementations, tools, and formats in use across different authors or technologies, but with the release of the W3C Design Tokens Community Group Draft Report, we can hopefully start to converge on at least a few key aspects.

This is a community group, and the output is more of a recommendation than an official platform spec. This article will refer to this as the “format”, “recommendation”, or “recommended format”.

Benefits

Here are a few of the positive impacts of design tokens in general, and their increasing coherence through industry alignment.

Centralize definitions

Arrows pointing to a central representation of design tokens

At the most basic level, design tokens exist to externalize design values to a central location that can be referenced broadly. Because they’re central, they can be modified for a given use case and will apply consistently. For example, a component framework can be implemented to reference a “brand-color” token instead of hard-coding the same color everywhere.

A button and checkbox, each referencing a “brand-color” token, with representation of the value changing from blue to green

Now we can simply change the value associated with the token, and the components follow.

Note the arrows all point to the left, which is an important distinction we’ll look at in “Design to code” in part 3. This flow indicates each specific use is referencing a known and expected less-specific definition.

Intended for interop

Illustration of a desktop, tablet, and phone

Centralized definition is what CSS preprocessors like SASS or LESS brought us, but the intent of design tokens is to be agnostic to the technology to increase interoperability. That is, the same token definitions could be used across Android, iOS, XAML, or various web frameworks. To achieve this, the recommendation defines JSON as the definition format.

Diagram showing JSON tokens in the middle, pointing out to Android, iOS, Web, and XAML consumers

Translatable

Illustration of design tokens being processed through a gear producing multiple output files

A common format is important, but the chosen JSON format is not natively rendered by presentation technologies. The way to handle this is through translation tooling, of which there are several options. Amazon’s Style Dictionary is very flexible, regularly maintained, and has many references across the industry. Salesforce’s Theo and Diez haven’t been updated in a couple of years, though they may still be viable for some uses. There’s also Specify, which is marketed as a “Design data platform” service, which seems promising but less open than other solutions. AdobeXD has also defined a Design System Package format. All these tools fit somewhere between the design definition and implementation.

Note that all tools may not support the recommended format at this time.

Illustration of json design tokens being processed through specific tools, producing examples of a token in the different consumer formats for Android, iOS, Web, and XAML

Current concerns

Even with the options available today, there are many gaps to fill and misuses to resolve.

No standard for naming

Following the many implementations, formats, and tools mentioned above, there is also no standard around the structure or naming of design tokens. This means that even as a common format gains support, there are no agreements in sight that will enable interoperability across design systems or component frameworks.

For example, here are the tokens for two popular design systems representing the brand color:

  • Google Material Design 3: md.sys.color.primary (md=Material Design, sys=System)
  • Salesforce Lightning: colorBrand or brand-primary (they have two sets?)

Given that there is often a need to mix and match component libraries, having no consistent way to apply design values poses some challenges. For example, consider combining two complicated components like a calendar from one library and a shopping cart from another. You would need to configure each component’s specific design tokens to coordinate the brand color, among any other attributes you might want to unify.

For a simpler case, there’s really no reason for many component frameworks to build yet another Button, but they will if that’s the only way to apply their design tokens.

No standard for applying tokens

Illustration of a design token pointing to a button, checkbox, and radio, with question marks

There are also no standards for how to apply tokens to components. For example, every component framework I can think of has a brand-colored Button, but there’s no universal way to connect the “brand-color” token (or whatever it’s called) to the Button’s fill.

The work happening under Open UI could provide some necessary portion of a solution to this. This project seeks to define a base anatomy for common components. For example, a Button has “content” and “root” parts. The “root” represents the main visual container of the Button, which is where the fill color is applied for this component. A reasonable representation of addressing this is “button.root.fill”.

The “Design to code” proposal in part 3 seeks to bridge design token definitions with UI implementations in a way that any components which support declarative anatomy can take advantage of declarative styling.

Not tracking the underlying decisions

Illustration of design decisions, picking rest, hover, and press state colors from a light to dark ramp and “4.5:1” and “3:1” indicating contrast requirements

The point of a design “system” is that many individual decisions come together for a cohesive outcome. Design tokens work well for defining specific values, but the underlying decision that produces a specific value is lost or not applied consistently. A great example of this is often seen in color choices for interactive components like Buttons or between light and dark modes.

Looking back to the brand color Button scenario, here are the “brand” rest and hover colors Salesforce defines as tokens:

Sample blue buttons in rest and hover state using different shades for the background. Rest and hover both have light text.

These colors are “blue-50” and “blue-30” respectively.

Here are the rest and hover colors for a “success” Button:

Sample green buttons in rest and hover state using different shades for the background. Rest has dark text and hover has light text.

These colors are “green-70” and “green-50”. The colors are still offset two spaces on the palette, but lighter, such that the success button requires flipping the text to meet contrast. Without context, this feels like an odd choice and there doesn’t seem to be a clear rule as to the selection of the “success” colors relative to “brand”.

Let’s look at one more example, for Salesforce’s “destructive” button:

Sample red buttons in rest and hover state using different shades for the background. Rest and hover both have light text.

Here they are “red-40” and “red-30”. No consistent pattern between where the colors sit on the palette or how much space is between them.

If a designer needed an orange button for a warning, what rule should they use to select the colors from the palette? There’s no way to know because the decisions that went into these examples aren’t described in the flow of the token references.

Potential to make individual changes that break downstream

Illustration of a design token, referenced by two other design tokens, one going to a button and the other to a radio button. The top design token is edited, the button is happy, but the radio is sad.

This gap builds upon the previous point about not tracking underlying decisions.

Every major design token implementation relies on aliasing or referencing “base” or “global” tokens to increase specificity of use. For example, the “background-success” color used for the rest state of the button above references “green-70”, which is assigned the actual fixed color value.

A designer needs to style a “success” slider and uses “background-success” for the fill. They realize that for a slider to be accessible it must meet 3:1 contrast with the container. The app background color of #f3f3f3 requires “green-50” to meet contrast so they change “background-success” from “green-70” to “green-50”.

Illustration of failing contrast on a slider, a common design token value being updated, then breaking contrast on a button.

Because the Button also references “background-success”, it now uses “green-50” for both the rest and hover state, which is unexpected, and worse yet the Button foreground color is no longer accessible. Because the “success” Button wasn’t used in this part of the experience, the designer didn’t notice it was broken.

There may be additional layers of aliases, which introduces more potential for unexpected changes.

Obviously, designers need to be very careful when changing underlying tokens, but this is a common pain point and tradeoff. Either you update the value to “fix” the system, or you deviate and create yet another intermediate token, which must then be tracked, understood, and used correctly.

No inherent support for theming

Illustration of a split rectangle with a sun over light and a moon over dark.

Customers are expecting more from the visual design of their experiences. At a minimum, they expect dark mode in addition to the historically default light mode. Many also rely upon accessibility features, including the ability to request increased contrast or reduced motion. These features are not commonly supported across the web currently, but the fact that they are built into the web platform means it’s time for experiences built for browsers to catch up.

Most design token systems don’t have a concept to support theming other than to completely override all token values. This is feasible for building a dark mode, but is a manual process which must be kept in sync with the base set of tokens or the light mode tokens it overrides. All colors must also meet contrast requirements. This approach is time-consuming, error-prone, and doesn’t scale to support true customization and personalization.

Using t-shirt sizes to express options

Illustration of small, medium, and large t-shirts.

Design tokens that represent a ramp of choices, like for spacing, border radius, and font size, are often expressed using t-shirt sizes like small, medium, and large. While the sequence is well known, it can break down in actual use and inevitable extension or modification in the future. For example, here are Material Design’s latest shape tokens:

Material Design’s shape scale from “no rounding” to “full”.
Material Design shape values.

One issue is the size token names impart meaning that doesn’t exist. For instance, “medium” happens to be in the middle, but it’s not the most commonly used, and by default applies only to a Card. Compare that to Lightning design where “medium” is only 4px and used as the default for many controls like Button and Text Inputs.

Another issue arises when additional sizes are needed. Ideally in a true “system” we can avoid this, but a token structure needs to support design without getting in the way. We can add on, like “extra-extra-large” or in between like “small-medium”, but this can get messy quickly.

Leads to copies of complicated CSS

Illustration of a checkbox, button, and radio referencing identical CSS files.

Styling components is complicated. There are many CSS attributes that must be applied, and CSS is finicky enough that if this isn’t done consistently you will get different results.

Consider a Button. It will at least have attributes for the “content” font and color, and the “root” background color. It will likely be stateful, so multiply this by “rest”, “hover”, “active”, “focus”, and “disabled”. It may also have a border with color, thickness, radius, and a dash pattern. It could have a drop shadow for elevation, some animation timing, some layout settings, it may be selected, and the list goes on.

Consider just one portion of that example, applying a background color for rest, hover, and active states. That’s three CSS selectors and three tokens, one for each. Then you go to test it and realize that even a Button in “disabled” state is responding to the “hover” state. That selector needs to apply only when the component is enabled. Then you realize the hover state isn’t responding correctly on a touch device. That selector also needs a media query.

Now you have to repeat that for every other component that may respond to hover and active states, may be disabled, may be used with touch, etc. And that’s only one example. Tokens are good for providing the values, but they’re not enough to make sure they are applied correctly and consistently.

Make sure to follow FAST for part 2 and part 3 (coming soon) that look at what we can do to solve this in a creative and flexible way.

--

--