Using Design Tokens to Accelerate Design Systems
How Intuit is improving the developer UX for design tokens
As a unified design system, we empower teams across Intuit to deliver seamless, cohesive experiences.
Because we serve both designers and developers, we needed to invest in tooling that would allow us to scale our impact in order to fit their needs.
Opportunity: How could our team manage complex, evolving design data that would support multiple products and platforms?
We knew having a centralized data source for generating design tokens was the key to:
- Making sweeping changes across multiple code repositories and design libraries
- Seeing a bird’s eye view for what values were being used at both a foundational and component-specific level for each product
- Creating closer parity between design and implementation
We also needed to account for the fact that not all teams are on the same tech stack and may have different requirements for implementation.
Design data management
Establishing a scalable system that could store design-related data was the first step in how we approached supporting our teams.
After reviewing a few options, we ended up choosing Style Dictionary, an open-source project from Amazon, which serves as a build system that generates design tokens.
From there, it was a matter of:
- Defining architecture that would support our existing values
- Adding audit logic to improve the stability of the system
- Providing a visual representation of design tokens
- Establishing logic to cascade data to match our system’s existing hierarchy & defaults
Contribution & Ownership
Creating the system was one thing, but making sure our team wasn’t a bottleneck was another. Keeping this in mind, we wanted to make sure the system was organized in a way so anyone could contribute towards their own product without introducing risk.
We did this by separating out our data into two main buckets:
- Durable, foundational data that shouldn’t have overrides
- Themeable data that could have overrides
Instead of a “restaurant” model where we would be taking orders, we encourage a “self-serve” model that enables teams to update their own data in their given theme folder.
This approach makes our design data management system similar to an open source project, where users contribute the changes they need while having our team act as repo maintainers.
Teams make sure their data is accurate. We make sure the system doesn’t implode. Win-win.
Iterating and improving
While we had developed a system that could handle robust data, the system is only successful if users actually use the tokens it generates.
We started by implementing tokens within a single component repository, paying attention to details such as:
- How do users know what tokens are available?
- What changes to the repo should users be aware of?
- Do they feel confident about which token to implement?
To facilitate adoption, our team was focused on ensuring we had created an easy experience for developers to implement design tokens.
From there, we could start onboarding more teams to the system and get feedback on improvements and learn of any gaps we needed to address.
Design token visibility
Since we don’t actually store the output of our build system in GitHub, we needed a way to surface what gets generated so users could see what our system was actually offering.
Storybook
We already leverage Storybook as a way to showcase our components as part of our Design Systems CLI, so it was the natural tool of choice to showcase our design tokens as well.
We’ve even created a custom Storybook addon that lets us define persistent theme and color mode data while users navigate different stories.
The experiment column highlights our intent of making sure that any team can add what they need and flagging the data as experimental, even if those values aren’t officially defined as part of our design system.
Using Storybook’s addon knobs, we can provide users the ability to filter tokens based on criteria we have defined using a combination of our architecture and custom Style Dictionary logic.
In addition to what’s shown in the screenshot, we’re also able to provide descriptions on the usage of the token. This is super helpful for informing teams on how the token should be implemented.
The best part of this setup is that it is completely automated and will update based on any changes to the repo. What you see is what you get!
Making changes with confidence
When updating design token values, it can be a little nerve-wracking since those changes could impact multiple components.
It can also be time-consuming to make design token updates, push those changes, and have to test in a separate component repo, only to find out you made a typo.
Our team solved this issue by creating a Storybook preview that showcased components using the canary release version of a pull request.
We typically use this feature to check that updating a token value wouldn’t visually break a component (and make our components better in the process!).
Mitigating risk
Semantic versions help inform teams on what to expect when they update their version of design tokens.
Auto
By using Auto, another Intuit open source project, we can easily associate our releases with what type of changes we’re making using GitHub labels:
patch
: token values changeminor
: new tokens addedmajor
: token names changed/tokens deleted
Because patch
and minor
versions introduce no breaking changes, teams have the ability to automatically consume the latest version by using ^
in their dependency settings if they’d like.
This enables teams to feel confident that no breaking changes will occur for non-major releases, and we can use a tool like Renovate to get these dependency changes automatically.
Streamlined implementation
Throughout this process, we went through several different “proofs of concept” to establish different ways users could implement design tokens.
We started small, testing subsets of tokens from their categories like:import {colorBlack} from @package-name/color
However, this proved to be tedious and we began to test other strategies that:
- Made the import process less painful
- Kept bundle-size down (through tree-shaking)
- Reduce the amount of back and forth required
By collaborating with developers across our organization, we built up a list of requirements that helped inform what type of customizations our system needed to support.
Once we felt confident that we were able to generate accurate design tokens from extensible data, we began exploring how we could make further improvements to implementation and remove barriers through tooling.
PostCSS-themed
PostCSS-themed is an open source project we created to help support multiple themes, using CSS files.
A user could set non-themeable values in a CSS file and then set their themeable values through theme.ts
. This was technically working, but there was an issue with this approach: users would need to hop back and forth between these two files to define their design token usage.
We solved this problem by adding new functionality to our plugin: postcss-themed
would now be able to support objects… which means we could implement component-specific design tokens directly in CSS! 🎉
Instead of developers having to:
- Assign variables in CSS
- Define those variables in a separate theme file
- Cross-reference tokens with what’s in the tokens Storybook…
Now we’ve unlocked the ability for developers to implement a design token that points directly to a component’s attribute in ONE CSS file:
font-weight: @theme link.linkTextDefaultFontWeight;
And because the data from our system supplies values for multiple themes, we no longer have to manually store those definitions in our component repos.
Custom VS Code extension
We wanted to meet developers where they were: in their IDE. The best way to accomplish this was through a custom VS Code extension.
Part of our acceptance criteria was to:
- Allow users to auto-complete tokens based on what was available
- Show a snippet of the token’s value
- Bonus points: color previews
Using this extension, developers can now quickly search through available tokens within VS Code and confirm the value that the token was pointing to, which meant we had improved:
- Speed (autofill)
- Confidence (token value)
- QA (visual preview)
It’s sped up our implementation workflow significantly, since the design token and its context are in the same place. This also helps with troubleshooting, since we can quickly identify if a token is being used in the wrong slot.
While the extension is currently internal-facing only, we’d like to eventually make it open-source.
What’s next
Having a centralized design data source has amplified our design system by acting as a unifying thread that connects products, platforms, design, and code.
Our team is excited about contributing more design tokens tooling including:
- Figma plugins to improve the designer/developer handshake process
- Enhanced automated visual testing around token implementation
- An adoption metrics dashboard
While there are still plenty of ways we can improve how our system works, I am thrilled at the progress we’ve made and the potential we’ve unlocked.
This project wouldn’t be the success that it is, if it weren’t for these wonderful folks: Adam Dierkens, Adil Malik, Andrew Lisowski, and Tyler Krupicka!