Creating Themeable Design Systems

TL;DR: Design systems + CSS Zen Garden = Awesome.

Buttons in a design system with custom themes applied to them

Is it possible to create a single design system that powers wildly different-looking brands and experiences? The answer is yes! But why would you want to?

  • Different audiences and contexts – The team at GE recently discussed the opportunities and challenges of sending their Predix design system to different audiences within their gigantic, diverse organization. They’re working hard to deliver a thoughtful system that powers a diverse set of use cases: software that powers wind turbines, software for aviation, and software that surely powers a bunch of other crazy stuff.
  • Different brands – I’m currently working with a large publisher to create a design system to power their 20+ unique, iconic brands. The organization wants the ability to share all components (including image galleries, social share widgets, cards, etc) across brands, while not compromising each brand’s individual identity in the process.
  • Different products – I’ve worked with a number of software companies who draw a pretty clean line between their marketing properties and their software products. While there are certainly some valid reasons for this split, in my view there’s a big opportunity to create a design system that can serve these seemingly-different products. If marketing and product properties are all powered by the same system, the brand identity carries through the entire experience and users can benefit from consistent UX patterns across the organization’s universe.

CSS Zen Garden

For the uninitiated, CSS Zen Garden is a site by Dave Shea that demonstrates how designers can take the exact same HTML markup and write CSS to achieve wildly different designs. It was a huge, collective “a-ha!” moment for the industry, as it helped everyone understand the power that is unlocked by separating concerns between structure, style, and behavior.

There’s a finite number of ways to make a functional front door to a house, but there are limitless aesthetic possibilities: paint color, handle style, trim, and other design flourishes. (To underscore this point, check out Yesenia Perez Cruz’s wonderful Instagram account for gorgeous examples of building exteriors.). Each component in a design system contains its own structure, style, and behavior. In my own design system work, I’ve found there’s often a finite number of ways to structure a component (with HTML) and make it interactive (with JavaScript), but the styling possibilities are seemingly endless.

The aesthetic layer is often the most malleable layer of the frontend stack, which means that we can create systems that allow for a lot of aesthetic flexibility while still retaining a shared underlying structural foundation.

But design systems are killing creativity, man!

Bullshit! An extremely shallow reading of design systems is that they put any and all aesthetic flexibility on lock down (“You can have any color as long as it’s black” – Henry Ford, design system manager at Ford Motor Co.) , and relegate visual designers to a life of mindlessly assembling puzzle pieces. The truth is that a design system is as rigid or as flexible as you make it to be. It’s true that design systems introduce constraints to digital design in order to reign in the special snowflake epidemic that tends to permeate organizations. But it’s entirely possible to create a thoughtfully express a multitude of art directions in such a way that works with the grain of the design system. (For more on this topic, myself and Sophie Shepherd had a great time addressing this misinformed perspective on a webinar).

Making Themeable Design Systems Happen

So how do we actually go about creating design systems that can serve wildly different brands, audiences, and contexts? I’ll walk through how I’ve approached these problems in my own work.

Design Tokens and a Multi-Tiered Variable System

From Salesforce’s Lightning Design System, which popularized the term “design tokens” in design systems:

Design tokens 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) in order to maintain a scalable and consistent visual system for UI development.

Design tokens move variables to a higher level, making it easier to manage brand attributes without diving deep into a codebase. For a design system powering multiple brands, each brand defines their own design tokens, which then hook into the design system’s codebase. To accomplish this in my own work, I tend to break these variables into several tiers:

Tier 1: Brand Definitions

First tier variables define the brand’s core attributes, serving as the raw materials for the UI’s visual design. So things like:

--color-brand-starbucks-green: #00704a;
--color-brand-target-red: #cc0000;
--font-family-primary: 'Roboto', sans-serif'

At this tier, we’re only defining these values in the abstract and not applying them to parts of the UI.

Tier 2: High-level application variables

The second tier takes the first-tier variables and starts applying them to high-level applications within the UI. Second-tier variables look something like:

--primary-action-background-color
--border-color-subtle
--font-family-heading

We can now map those first-tier variables to those second-tier variables to create a (hopefully legible) system for how to use those raw materials defined in the first tier.

--primary-action-background-color: var(--color-brand-starbucks-green);
--border-color-subtle: var(--color-gray-07);
--font-family-heading: var(--font-family-secondary);

Tier 3: Component-specific variables

The third tier is component-specific variables, which are found in each component’s partial. such as:

--button-background-color
--button-font-family
--button-border-width

We can now map the high-level application variables to specific components. So for a button component, we might see something like:

--button-background-color: var(--primary-action-background-color);
--button-font-family: var(--primary-action-font-family);
--button-border-width: var(--border-color-subtle);

These component-level declarations aren’t likely to change once they’re put in place, which makes all the theming stuff happen at a higher level. That saves a lot of agony if you’re trying to create components meant to serve several dozen brands.

Another neat thing is that with a hierarchy like in place, the team can swap out variables and see those changes ripple throughout the design system. First instance, a team might do some research and testing to find that users are far more likely to click blue things rather than the UI’s current yellow color. Rather than combing through the entire codebase, the team can update the value of --primary-action-background-color to --color-brand-blue and watch all the components meant for primary action switch from yellow to blue.

It’s worth noting that this multi-tiered variable strategy may be overkill for some systems, and can slow down the team’s speed while the system is being created. In my experience, we’ve started the project with a leaner variable structure, then introduced additional tiers once the dust has settled a bit. Getting to a three-tiered system like this is a great long-term strategy, but in my experience it’s been hard to architect this setup and design/build components at the same time. You can read more about a different flavor of tiered variables over on CSS Tricks.

Atomic Design

Atomic design is a methodology for creating thoughtful UI design systems: all of the vastness and complexity of our UI universe can be broken down into a finite set of atomic elements. Components are included inside of more complex components, which are in turn included inside even more complex components. This Russian-nesting-dolls approach to UI design systems helps teams thoughtfully create the whole experience and the parts of the whole at the same time.

Button Atom

Take the humble button. Buttons come in all sorts of shapes and sizes, but they can ultimate consist of the same set of shared properties. They contain text (or not), might have an icon after or before the text, and may have several stylistic variations that can be handled via style modifiers (such as c-btn--large).

Using the variable system described above, we can define the button atom‘s visual styles for 5 unique brands. I created a Codepen so you can see this in action.

Buttons in a design system with custom themes applied to them

Card Molecule

A molecule is a component made up of a handful of atoms. A card molecule is a common component that can house all sorts of different content. I created a Codepen to see how to create a theme-able card component using variables:

Card component with 5 distinct aesthetic styles applied to it

Card Grid Organism

An organism is a complex component that’s made up of atoms, molecules, or other organisms. A grid of cards is a common pattern you see on a whole bunch of sites, so I created a Codepen to show what a theme-able card grid looks like:

A card grid component with 5 distinct styles applied to it.

Now that we have CSS Grid at our disposal, we have all sorts of wonderful, pure-CSS opportunities for laying out a grid of cards. I highly recommend Rachel Andrew’s Grid By Example, her book on the topic, Jen Simmons’ wonderful Labs and her new Layout Land video series for inspiration on CSS Grid patterns.

For each brand, we can control spacing between items, define entirely different grids, and create unique layout patterns. It’s worth noting For a real project, I’d likely create variations of this layout component (l-grid, l-grid--fancy, l-grid--staggered and so on) for a bit more predictability, but the point is that individual brands can control their own distinct layout system.

Additional Levers for Flexibility

I’ve written before about all the different ways a pattern can be manipulated in order to create unique experiences.

  • Content – the text, images, and media you pour inside your UI components obviously influence how the UI components look.
  • Structure – A button can have an icon attached to it, or not.
  • Style – Style modifiers help teams create a thoughtful suite of component variations that can serve different use cases.
  • Behavior – A UI component could have different behaviors (such as a dismissible alert or card).

Combining all of these factors can lead to wildly different experiences that are all powered by the same system.

Our work with DotDash (formerly About.com) involved exploding their monolithic brand (About.com) into a suite of brands meant to better serve each site’s respective audience. We helped them establish these new brand identities, but also helped them establish a shared design system to power these unique, visually-distinct brands. Below is a card component from three of the sites we helped them make:

A card UI component featuring different photography, content, and aesthetic styles

A single card component applied to three brands: Verywell.com, TheSpruce.com, and TheBalance.com.

You can see that while the structure of the component is the same, the visual aesthetic is distinct for each brand. Pretty cool!

I think there’s a lot of power in combining a systematic approach to creating UIs with aesthetic flexibility. We can create a lot of efficiency by sharing components between brands while still honoring each brand’s unique visual identity. I hope this post helps dispel the myth that design systems impose stifling rigidity on design teams and lead to bland, one-size-fits-all experiences. If you’re working on theme-able design systems, feel free to get in touch!