Preprocessing Design System Tokens in CSS-in-JS

Don’t throw away your design system tokens!

Michael Paravano
4 min readApr 23, 2021
Photo by Josh Appel on Unsplash

You’ve decided to change your design system styles from LESS or SASS to a CSS-in-JS approach for its many advantages, but you’ve run into a problem.

The Problem

Your existing design system is built upon a library of design tokens that are being preprocessed in LESS or SASS to make your CSS styles consistent. The whole look of your design system is based on these tokens and with CSS-in-JS, you’ve lost the ability to use your tokens and preprocess them.

Or have you?!

One Way to Live

Let’s create a small example and say our tokens look like this:

One thing we CAN do to solve our problem is this: Create and distribute a tokens package to be consumed as a dependency for our components.

Let’s say this is a styles.js file for our banner component that imports that tokens package:

While that will work, it’s less than ideal because teams that are consuming our packages will not necessarily update all packages at once. That means we will almost certainly end up with many very slightly different versions of our tokens package in the app bundle where there is lots of overlap.

For example, let’s say a team is using v1.0.0 of our button and banner packages which both use v1.0.0 of our tokens package as a dependency.

Now that team decides to update its banner package to v1.1.0 which has an updated dependency of tokens v1.1.0.

It would not be uncommon to see the tokens package in that team’s bundle twice. Imagine if that team were consuming a very realistic number of say 20 packages instead of just two!

In the end, we’ve traded one problem for another. That’s one way to live, but there’s a better way.

The Real Solution

We can preprocess our CSS-in-JS files with css-in-js-preprocessor:

It works by using the same concept that you’re familiar with for LESS and SASS. To illustrate that, let’s modify our previous example:

First of all, you’ll need to install css-in-js-preprocessor:

npm i css-in-js-preprocessor --save-dev

This time around, our tokens are going to live in a file in our project called my-tokens.json. The content is the same as above, it’s just in a file instead of a package. Now, we change the import of our banner style.js file to import a local file:

Next is where the magic happens. We’re going to create a style-preprocess.js file which will be executed at the end of our compile or build process on the files that are outputted from that process:

How you execute this file in your pipeline is up to you, but we typically do it as part of a compile script that we execute as an NPM command in our package.json:

"scripts": {
"compile": "tsc && node ./style-preprocess.js"
}

The tsc command is to compile TypeScript. If you’re not using TypeScript, you are most likely using Babel. Just add on our new script to the end of whatever you’re doing to transpile your JavaScript.

WARNING: Make sure you run the script against the files the build process outputs and not against your source files!

What we should see is that our styles.js file changed from this:

…to this:

BAM!

We have successfully preprocessed our files by replacing the references to the tokens with the actual token values! Not only that, but css-in-js-preprocessor has removed the token import from the top of our file!

Our original source file for styles.js should be unchanged and using the tokens. The benefit is just like it was with LESS and SASS: if the token value ever changes, we simply recompile and our output now has the updated values.

No dependencies or versions to manage. No duplicated code in our bundle. No muss. No fuss.

Multiple Files

In our real life code, we’re almost certainly going to need to preprocess more than one file. Well, the preprocessor conveniently returns a function so we can setup our base preprocessor one time and loop through the files we want to preprocess:

Custom Preprocessors

You’ll find that css-in-js-preprocessor is even more more powerful and can be used in many more ways and for many more things than our old LESS and SASS counterparts. This is because we can inject our own custom preprocessors.

Let’s create two custom preprocessors and add them to the preprocessor util. This replaces lines #8–9 above:

If we re-run our compile, we should see that our styles.js file has changed to this:

We can see that padding has been transformed to margin and a date/time stamp has been added as a comment to the bottom of the file due to our custom additions.

This is obviously a contrived example, but it illustrates how you can do anything you want. You are limited by only your imagination.

For More Than Just CSS-in-JS Files

Because we can add our own custom preprocessors, we can actually use this for more than just our design tokens and CSS-in-JS.

For example, one thing I often do is create a version constant in my component files that gets added to the outermost DOM node of a component as a data attribute when it’s in debug mode. This version constant gets updated during my bump process and runs against .jsx and .tsx files.

Let’s see LESS and SASS do that!

Onward

We can now move our design system into the new age of CSS-in-JS, reap all of its benefits, not have any drawbacks, and gain additional horsepower & flexibility along the way!

--

--

Michael Paravano

Senior Front-end Engineer specializing in all things front-end software development.