Learn how to ship a design tokens pipeline to production in my ebook

Design

Integrating Design Tokens With Tailwind

Last Updated: 2021-02-15

Table of Contents

1 | Introduction to Design Tokens
2 | Managing and Exporting Design Tokens With Style Dictionary
3 | Exporting Design Tokens From Figma With Style Dictionary
4 | Consuming Design Tokens From Style Dictionary Across Platform-Specific Applications
5 | Generating Design Token Theme Shades With Style Dictionary
6 | Documenting Design Tokens With Docusaurus
7 | Integrating Design Tokens With Tailwind
8 | Transferring High Fidelity From a Design File to Style Dictionary
9 | Scoring Design Tokens Adoption With OCLIF and PostCSS
10 | Bootstrap UI Components With Design Tokens And Headless UI
11 | Linting Design Tokens With Stylelint
12 | Stitching Styles to a Headless UI Using Design Tokens and Twind

What You’re Getting Into

I thought I was finished with my design tokens article series. However, I ended up using a combination of Style Dictionary and Tailwind in a side project, and I wanted to show you all the solution.

Tailwind is a popular tool for adding styles to a web application.

Tailwind shifts from writing your CSS in a global file, CSS modules, or inline JavaScript.

Instead, you compose the styles you need by applying “utility classes.” Utility classes are pre-defined classes that represent a style that you want to be applied to an element.

Here’s an example transformation from CSS to Tailwind’s utility classes.

button {
  color: red;
  padding: 1rem;
}

…becomes

<button class=“text-color-red p-4”>
  Some Button
</button>

As you develop, you will have to check the Tailwind documentation to see which utility classes that you can use to apply the style you desire.

integrating design tokens with tailwind

It takes some getting used to, but it gets easier and easier with time. There’s also a handy VSCode extension that provides some IntelliSense.

integrating design tokens with tailwind

To summarize, Tailwind allows you to style your application by composing utility classes. A utility class applies a design specification, such as a color of red, to an element.

If you’ve been reading about design tokens, your Spidey senses should be tingling.

Each utility class effectively applies a design token, a key-value pair representing a design specification.

Integrating design tokens into Tailwind is therefore a natural fit.

Given that Tailwind allows you to override or extend its utility classes, we can map design tokens generated by Style Dictionary to utility classes.

In this article, we’ll get a taste of how we can do just that.

Integrating Design Tokens Into Tailwind

The Setup

In previous articles, I’ve written about how to automate the process of generating design tokens from a Figma file and transforming them with Style Dictionary and consuming the transformed design tokens across consuming appications.

By the time of reading this article, we’ve had enough practice with the automated process and can start this tutorial with an existing project that contains design tokens exported by Style Dictionary.

Here is a starter repository that you can fork to follow along with the rest of the tutorial:
https://github.com/michaelmang/design-tokens-tailwind

The starter repository contains a simple React app with the transformed tokens stored in tokens/tokens.js.

/**
* Do not edit directly
* Generated on Mon, 15 Feb 2021 16:35:23 GMT
*/

module.exports = {
  "color": {
    "primary": {
      "value": "#3a4d7e",
      "type": "color",
      "original": {
        "value": "#3A4D7E",
        "type": "color"
      },
      "name": "colorPrimary",
      "attributes": {
        "category": "color",
        "type": "primary"
      },
      "path": [
        "color",
        "primary"
      ]
      },
    "primary_light": {
      "value": "#b9c4df",
      "type": "color",
      "original": {
        "value": "#B9C4DF",
        "type": "color"
      },
      "name": "colorPrimaryLight",
      "attributes": {
        "category": "color",
        "type": "primary_light"
      },
      "path": [
        "color",
        "primary_light"
      ]
    },
    "black": {
      "value": "#29322e",
      "type": "color",
      "original": {
        "value": "#29322E",
        "type": "color"
      },
      "name": "colorBlack",
      "attributes": {
        "category": "color",
        "type": "black"
      },
      "path": [
        "color",
        "black"
      ]
    },
    "black_light": {
      "value": "#404f48",
      "type": "color",
      "original": {
        "value": "#404F48",
        "type": "color"
      },
      "name": "colorBlackLight",
      "attributes": {
        "category": "color",
        "type": "black_light"
      },
      "path": [
        "color",
        "black_light"
      ]
    },
    "white": {
      "value": "#ffffff",
      "type": "color",
      "original": {
        "value": "#FFFFFF",
        "type": "color"
      },
      "name": "colorWhite",
      "attributes": {
        "category": "color",
        "type": "white"
      },
      "path": [
        "color",
        "white"
      ]
    },
    "white_dark": {
      "value": "#f1f5f8",
      "type": "color",
      "original": {
        "value": "#F1F5F8",
        "type": "color"
      },
      "name": "colorWhiteDark",
      "attributes": {
        "category": "color",
        "type": "white_dark"
      },
      "path": [
        "color",
        "white_dark"
      ]
    },
    "error": {
      "value": "#eb6957",
      "type": "color",
      "original": {
        "value": "#EB6957",
        "type": "color"
      },
      "name": "colorError",
      "attributes": {
        "category": "color",
        "type": "error"
      },
      "path": [
        "color",
        "error"
      ]
    },
    "warning": {
      "value": "#f2c94c",
      "type": "color",
      "original": {
        "value": "#F2C94C",
        "type": "color"
      },
      "name": "colorWarning",
      "attributes": {
        "category": "color",
        "type": "warning"
      },
      "path": [
        "color",
        "warning"
      ]
    },
    "info": {
      "value": "#aac2ff",
      "type": "color",
      "original": {
        "value": "#AAC2FF",
        "type": "color"
      },
      "name": "colorInfo",
      "attributes": {
        "category": "color",
        "type": "info"
      },
      "path": [
        "color",
        "info"
      ]
    }
  }
};

These design tokens could be generated from a JSON representation like this:

{
  "color": {
    "primary": {
      "value": "#3A4D7E",
      "type": "color"
    },
    "primary_light": {
      "value": "#B9C4DF",
      "type": "color"
    },
    "black": {
      "value": "#29322E",
      "type": "color"
    },
    "black_light": {
      "value": "#404F48",
      "type": "color"
    },
    "white": {
      "value": "#FFFFFF",
      "type": "color"
    },
    "white_dark": {
      "value": "#F1F5F8",
      "type": "color"
    },
    "error": {
      "value": "#EB6957",
      "type": "color"
    },
    "warning": {
      "value": "#F2C94C",
      "type": "color"
    },
    "info": {
      "value": "#AAC2FF",
      "type": "color"
    }
  }
}

By configuring Style Dictionary to use the js transform group and the javascript/module formatting, we get the final tokens as seen in src/tokens/tokens.js of the project.

The javascript/module format exposes a CommonJS module. We will have to prefer this format over the javascript/es6 format since Tailwind’s configuration works with CommonJS modules.

This is too bad because the javascript/es6 format is preferable given that it exports a JavaScript key-value pair that reflects a design token more closely.

I have already initialized the project with Tailwind which you can easily do following the documentation for installing with Create React App.

The Implementation

For our tutorial, we are going to override the default colors that come with Tailwind with the tokens that are in the tokens.js file.

The place where we can override Tailwind is in the tailwind.config.js file.

If we want to override the theme colors, we would add a colors property with nested properties representing the colors:

module.exports = {
// ...
  theme: {
    colors: {
    primary: '...',
    secondary: '...',
    // all other colors
    },
    extend: {},
  },
  // ...
}

We won’t be doing this, but alternatively, you could nest this colors object within the extend object if you wanted to add more colors while keeping the defaults.

Each property within colors is essentially a design token. So, we’ll place our design tokens there.

Since both this file and our tokens are JavaScript, we can import our tokens into the tailwind.config.js file and set them as the value of the colors property.

Let’s try by just importing the tokens and logging what we are working with:


  const tokens = require('./tokens/tokens');
  console.log(tokens);

Run node tailwind.config.js and we can see that we do in fact have access to all the tokens. We will need to transform this object to just be an object with key-value pairs like:

{
  primary: '#3a4d7e',
  // ...
}

Given the structure of the current tokens object, we will need to have our new object use the attributes.type value nested under each color property (i.e. color.primary, color.secondary) as the keys and the value on each color property as the value.

Note, I’m suggesting that we use the attributes.type in place of the name for each color property since we don’t need the “color” substring to be included. Practically, this means we can reference a color like text-primary as opposed to text-color-primary which is how the Tailwind defaults work.

Back in our tailwind.config.js file, let’s write some code that iterates through the imported tokens object and creates a new object with the desired format we just described.

First, we can transform our object into a collection of the color property values:

const tokens = require('./tokens/tokens');

const colors = Object.values(tokens.color);

This is what colors contains:


[
  {
    value: '#3a4d7e',
    type: 'color',
    original: { value: '#3A4D7E', type: 'color' },
    name: 'colorPrimary',
    attributes: { category: 'color', type: 'primary' },
    path: [ 'color', 'primary' ]
  },
  {
    value: '#b9c4df',
    type: 'color',
    original: { value: '#B9C4DF', type: 'color' },
    name: 'colorPrimaryLight',
    attributes: { category: 'color', type: 'primary_light' },
    path: [ 'color', 'primary_light' ]
  },
  // ...
]

Now, let’s iterate through this collection and transform each object into a entries ([key, value] pairs). Again, the key is the attributes.type and the value is the value.

const tokens = require('./tokens/tokens');

const colors = Object
.values(tokens.color)
.map(({ attributes, value }) => [
  attributes.type, value
]);

colors now contains:

[
  [ 'primary', '#3a4d7e' ],
  [ 'primary_light', '#b9c4df' ],
  [ 'black', '#29322e' ],
  [ 'black_light', '#404f48' ],
  [ 'white', '#ffffff' ],
  [ 'white_dark', '#f1f5f8' ],
  [ 'error', '#eb6957' ],
  [ 'warning', '#f2c94c' ],
  [ 'info', '#aac2ff' ]
]

Next, we can transform these entries into an object by using Object.entries:

const tokens = require('./tokens/tokens');

const colors = Object.fromEntries(Object
  .values(tokens.color)
  .map(({ attributes, value }) => [
    attributes.type, value
  ]));

colors is now the expected format for Tailwind:

{
  primary: '#3a4d7e',
  primary_light: '#b9c4df',
  black: '#29322e',
  black_light: '#404f48',
  white: '#ffffff',
  white_dark: '#f1f5f8',
  error: '#eb6957',
  warning: '#f2c94c',
  info: '#aac2ff'
}

Finally, we’ll need to make sure that the keys are in kebab case:

const kebabcase = require('lodash.kebabcase');
const tokens = require('./tokens/tokens');

const colors = Object.fromEntries(Object
  .values(tokens.color)
  .map(({ attributes, value }) => [
    kebabcase(attributes.type), value
  ]));

Make sure to install kebabcase:

yarn add lodash.kebabcase

Finally, we can nest this new object within the theme object of the Tailwind config:

const kebabcase = require('lodash.kebabcase');
const tokens = require('./tokens/tokens');

const colors = Object.fromEntries(Object
  .values(tokens.color)
  .map(({ attributes, value }) => [
    kebabcase(attributes.type), value
  ]));

module.exports = {
  purge: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
  colors,
  extend: {},
  },
  variants: {
  extend: {},
  },
  plugins: [],
}

As a final test, we can update the src/App.js file and try to utilize a Tailwind utility classes that uses the colors from our configuration:

- <header className="App-header">
+ <header className="bg-black-light">

Let’s run npm start and test these changes!

integrating design tokens with tailwind

Woohoo! We’ve successfully integrated design tokens from Style Dictionary with Tailwind. 🎉

Here's the final code:

https://github.com/michaelmang/design-tokens-tailwind/pull/1

Conclusion

I hope this article helps you put a foot forward towards integrating Tailwind within a design tokens pipeline.

In my next article, I propose an alternative to the normal design to developer workflow as I've described so far.

I’ll be releasing the articles as an ebook with some additional goodies. Subscribing to the newsletter will be the best way to get notified if you are interested.

As always, discuss, pow, and share!

Design Systems for Developers

Read my latest ebook on how to use design tokens to code production-ready design system assets.

Design Systems for Developers - Use Design Tokens To Launch Design Systems Into Production | Product Hunt

Michael Mangialardi is a software developer specializing in UI development with React and fluent in UI/UX design. As a survivor of impostor syndrome, he loves to make learning technical skills digestible and practical. Formerly, he published articles, ebooks, and coding challenges under his brand "Coding Artist." Today, he looks forward to using his mature experience to give back to the web development community. He lives in beautiful, historic Virginia with his wife.