Design Tokens in Components with React and Bit

ni
nitsan7703 months ago

A picture is worth a thousand words

A live demo is even better.

Before we dive into the details, let's look at how design tokens with Bit can help us build a better DX (designer experience).

Below, we have two Header components. The first is used on our Community website, Bit.dev, and the second is used on the website you are currently browsing - The Bit Blog.

Above, you will see a selection of color pickers. These correspond with some of the options in our base theme design tokens. Try selecting different colors and watch as the colors update across the parts of both Header components.

We have variations of the Header component, and even though they are independent components that function on different sites, updating a token in one place propagates those updates across all components.

The problem for product teams

When building software, product teams face a myriad of issues. While such issues propagate across stakeholders, some of the leading friction causes occur during collaborative efforts between design and development. Product stakeholders typically do not share a common language, tools, or a common source of truth.

Designers work with tools such as Figma/Sketch/Adobe/[insert design tool]. Developers work with tools such as VSCode/WebStorm/Atom/[insert IDE].

For designers, a component is a visual image; for developers, a component is code, likely written in JavaScript and React/Angular/Vue.js/[insert frontend framework].

One speaks of code; another speaks of visual language.

Roses are #ff0000, Violets are cta-background-purple-500

Design teams find themselves at the mercy of engineering teams when having to translate visual images and a design guide into components and code. This leaves us susceptible to inconsistencies, mistakes, compromises, etc.

Furthermore, this code is often built and compiled into a single package, making it even more difficult to reverse the inconsistencies, mistakes, and compromises mentioned above.

However, it isn't all doom and gloom, as modern design practices have facilitated better collaboration amongst design and engineering teams.

Enter design tokens

We first started to hear publicly about design tokens in 2014.

Created by Salesforce's Design System team, design tokens, in their words, "are the visual design atoms of the design system — specifically, they are named entities that store visual design attributes. We use them in place of hard-coded values (such as hex values for color or pixel values for spacing) to maintain a scalable and consistent visual system for UI development."

With this in mind, a component will be composed of a set of design tokens.

Let's take a moment to look at the example of a design token. Below is the list of design tokens used in the Base Theme here at Bit.

Please make sure that the Bit binary is installed on your machine:

npx @teambit/bvm install
Copied

You are more than welcome to fork our base theme component:

bit fork teambit.design/themes/base-theme
Copied

First, let's look at base-theme-schema.ts. It is the glossary for our design tokens.

You'll notice that there are roughly three kinds of tokens:

  • nameOfTokenColor. For example: backgroundColor.
  • onNameOfTokenColor. For example: onBackgroundColor.
  • nameOfTokenStateColor. For example: borderMediumHoverColor.

The first one is the color that is used as the background. Second, we have tokens that begin with the word on. This signifies that the color will be used on top of the first color. In the base-theme component, the backgroundColor is white (#FFFFFF), and the onBackgroundColor is black (#2B2B2B). The third type of token indicates a specific state: hover, active, etc.

You will notice that token names for these color properties are not intrinsically linked to the color itself but rather the purpose it serves within the branding. This creates a self-documenting system where each token is an entity that can be overridden if required, while still maintaining its naming. In other words, we have not assigned a token the name of purple simply because its initial color is purple.

If we were to set up the token to change color when given a different theme (eg. a dark theme), the color might no longer be purple but still has a token name of purple - rendering the naming convention ineffective and susceptible to confusion.

The benefits of design tokens

Salesforce realized that if you establish a new data layer on top of your visual language elements and manage them from a single source of truth, you could use a system to scale to all platforms consistently. With this new data layer in the form of design tokens, product stakeholders, namely design and development, can work together and collaborate over a single source of truth.

Design tokens can be created so that they can be parsed according to the device. In other words, you can send JSON values to web and iOS, XML to Android, etc. They can be transformed and formatted to meet the needs of any platform. This decoupling of design decisions from platforms and languages facilitates consistency at scale.

Doing so creates a flexible system for designers to become active contributors directly over code, but without risking or compromising developer control and velocity. Design teams will own the tokens. With this comes a reliance from designers to ensure that tokens are named correctly, are used in the correct places, are not misused, and are in sync between design tools and code. With great power comes great responsibility.

Fortunately, this responsibility can be delegated to components.

How we use design tokens at Bit

Design Tokens are an API between devs and design. Bit and design tokens are a very powerful combo.

At Bit, we decouple token from theme and pass tokens to components through a theme provider. With this, we can now update a single theme prop in a single component and the change will propagate throughout the application. An update to a single component can propagate to every relevant page on different apps throughout the organization and ensure that the organization keeps brand and design consistency at every touchpoint.

Components can be used and composed into many different applications and used by many other teams. This means the same component can "live" in a hundred various applications, and every change to it will change all of them.

With Bit, we can create greater levels of granularity for design systems. The design tokens that feed the atoms in a design system can be split up according to your own needs. You may choose to split design tokens into multiple components based on value types such as typography, size, color, grid, etc. Each component can be independently tagged and versioned, making it easier to manage subsections of a design system.

As a single change propagates through the system, ongoing visual consistency can be guaranteed across every touchpoint, page, and application. And if something needs to be reverted, it's as simple as rolling back a version of the component.

Distributed design tokens are especially powerful, limiting updates to a design system down to the granular level. For example, when handling internationalization or line-height differences amongst different languages, you can make specific adjustments right at the point of use, rather than having to update an entire library of design tokens in a monolithic structure.

Using design tokens in Bit

We start by defining the values for our design tokens:

import { BaseThemeSchema } from './base-theme-schema';

/**
 * maintained by design tokens go here!
 * the designer.
 */
export const baseThemeDefaults: BaseThemeSchema = {
  backgroundColor: '#FFFFFF',
  onBackgroundColor: '#2B2B2B',
  borderMediumColor: '#9598A1',
  onBackgroundMediumColor: '#707279',
  onBackgroundHighColor: '#2B2B2B',
  ...
};
Copied

Now that we have defined the values of the design tokens, we can use them in our createTheme function.

const { useTheme, ThemeProvider } = createTheme<BaseThemeSchema>({
  theme: baseThemeDefaults,
});
};
Copied

This function - created by Uri Kutner - returns a useTheme hook and a ThemeProvider component.

After wrapping your app with the ThemeProvider, you gain access to the theme values inside of our components by using the useTheme hook:

const ButtonStyledWithJs = () => {
  const { backgroundColor, onBackgroundColor, borderMediumColor } = useTheme();

  const jsVars = {
    backgroundColor: backgroundColor,
    color: onBackgroundColor,
    borderColor: borderMediumColor,
  };
  return <button style={jsVars}>A Button Styled using JS Vars</button>;
};
Copied

Another way to access the values is by using CSS variables. All of the components wrapped with ThemeProvider will access the theme values by using the var keyword. For instance:

const ButtonStyledWithCss = () => {
  /*
   * the `createTheme` function generated these CSS props. They are made available on the page by the generated Theme component.
   */
  const cssProps = {
    backgroundColor: 'var(--background-color)',
    color: 'var(--on-background-color)',
    borderColor: 'var(--border-medium-color)',
  };
  return <button style={cssProps}>A Button Styled using CSS Props</button>;
};
Copied

Design tokens can be created so that they can be used agnostically for the rest of the application. If one product in your company was built with SCSS, and another product was built with CSS-in-JS, the same set of design tokens, can be used and translated to work across different technologies.

Even more wins for the organization

When a UI component uses a design token, this gives the team behind it (for example the design system team) the power to quickly ship, change, and control design across its teams and applications. This is a compelling thing to have for any sizeable modern organization.

At the same time, developers can spend more time working on creating business features and less on manual updates to styles. And when design tokens are coupled with theming, the time it takes to adapt and change the look and feel of an interface is significantly reduced.

Design tokens enable consistency across many products, teams, and touchpoints. Product owners can expect to see fewer tickets related to styling bugs and the potential for better collaboration between designers and developers. This creates a better experience for users and better results for the business.

A glimpse into the future

More and more features are being added to Bit each week. Soon, you will be able to edit design tokens directly from the web via Bit.cloud. Visual editors in workspaces will make it easier than ever for designers to influence the development process.

A designer will be able to change a design token, update a component version, tag and export right on the cloud. Ripple CI will run, and all apps and teams will get the new token update. Engineering teams and other stakeholders like PMs will be able to define permissions and rules for the update, with a granular overview and control over the process before changes are deployed.

Conclusion

Design tokens provide a gateway to a frictionless experience between designers and developers collaboration. Designers are given the power to create a language that can be more easily translated into code. Combined with tools such as Bit, those tokens can be propagated across components, applications, and systems.

Design tokens create a single source of truth that engineering teams can actively use. These can be made and used as code rather than copying and pasting values from style guides written in shared documents. Style guides will continue to exist, but designers can leverage design tokens to ensure consistency by taking ownership of their creations before handing them off to developers. This creates a unified workflow where all stakeholders and product users stand to benefit.