The Many Faces of Themeable Design Systems

Very rarely is exactly one design system created to serve exactly one product that expresses exactly one design language. Nearly all the design systems we’ve worked on require a high degree of flexibility in order to properly serve our clients’ needs. Some of this flexibility is achieved by variability in component structure, style, behavior, and content. But advanced aesthetic flexibility is often achieved by creating a themeable design system.

But what specifically are we talking about when we’re talking about a themeable design system? This post will talk about the different flavors of themeability that a design system may need to accommodate, including supporting:

  • Multiple brands
  • Sub-brands
  • White-labeling
  • Seasons or Campaigns
  • Multiple generations of a design language
  • Multiple platforms or product families (web app vs marketing website)
  • Dark and light themes

A three-tiered token architecture

Before diving into each of these scenarios, I’ll take a little time to explain the theming architecture that can support all of these scenarios. A three-tier design token architecture can flow a specific set of tokens through a set of design system UI components:

A diagram demonstrating a three-tiered design token strategy. In the left column, a token called `color-brand-starbucks-green` is defined. The center column describes Tier 2 token values, which include `theme-color-background-brand`, `theme-color-content-brand`, and `theme-color-border-utility-success`. The right column demonstrates Tier 3 token values, which are reserved for specific components.
An example of a three-tier token architecture mapping

Here’s a breakdown of each of the 3 tiers:

  • Tier 1 tokens define the theme’s design properties in the abstract, which serve as the raw materials for the UI’s visual language.
  • Tier 2 tokens are a semantic theme layer that map Tier-1 tokens to high-level usage within a UI.
  • Tier 3 tokens are specific to components and are mapped to Tier-2 tokens.

A few examples of this architecture in practice:

Tier 1: color-brand-green =>
Tier 2: theme-color-primary-background =>
Tier 3: button-primary-background
Tier 1: border-radius-large =>
Tier 2: theme-border-radius =>
Tier 3: card-border-radius

This architecture controls a brand’s look and feel in its own layer away from the core component library, which unlocks the ability to flow different design languages through a shared set of core components.

From a technical standpoint, we either create a design-tokens directory in our design system repository, or create a standalone repository to store our theme design tokens. The gist of each theme looks like this:

theme-name
--tier-1-definitions
----animation.json
----border.json
----colors.json
----etc
--tier-2-usage
--tier-3-component

We’ve used many tools over the years, but have largely settled on Style Dictionary to manage and transform our tokens. I’ll leave aside how this plays out in static design tools as that’s a whole other ball of wax!

With that architectural overview covered, let’s dive into what scenarios this architecture helps address.

Multiple brands

We’ve worked with Pfizer, DotDash, Condé Nast, and many other organizations to create design systems that can support the design languages of the organization’s myriad suite of brands.

Button component with 5 different aesthetic themes applied to it

Each brand shares the same bones: HTML, API, common CSS styles (things like display and positioning properties), and JavaScript behavior, while flowing wildly different aesthetics through those components.

The design token directory structure for supporting multiple brands tends to look something like this:

- core
- brand-theme-1
- brand-theme-2
- brand-theme-n

The design system team publishes the themes, and design system consumers would pull in the core tokens (which are shared across all themes) and the appropriate brand theme into their product in order to achieve the desired results.

core + brand-theme-n

Subbrands

An extra layer can be added to support sub-brands, which are brands that inherit from a parent brand but contain certain nuances or overrides in order to differentiate between sub-brands.

An example of sub-branding in New York City’s brand identity

Applied to themeable design systems, an additional sub-brand layer can be added in order to append or override token values in order to achieve the desired result. The sub-brand theme can be thrown onto the pile like so:

core + brand-theme + sub-brand-theme

White labeling

We worked with a company who creates and sells mortgage software to banks. Despite being third-party software, the banks understandably want the user experience to embody the bank’s brand and design language standards to communicate trust to their customers as they apply for and manage their mortgages.

Similar to sub-brands, white-label experiences take a core branded experience and append or override certain design properties (almost always focusing on color and typography) to achieve the desired result.

core + brand-theme + white-label-theme

The white-label-theme can be provided as a customer-specific configuration file, or can even be surfaced in a CMS in order for customers to control their own UI overrides (think of skinning Gmail or your Myspace theme back in the day).

Seasons or campaigns

We’ve worked with many retailers who require a high degree of design system flexibility to accommodate seasonal campaigns or promotions. For instance, the team may need to build an e-commerce website homepage for Valentine’s Day that features pink call-to-action buttons and a different typeface for promotional headings. It’s absolutely possible to accommodate these short-lived, wildly divergent design languages using a well-architected themeable design system.

The execution is identical to supporting sub-branded or white-label experiences, with campaign-specific theme values overriding or extended core and brand values:

core + brand-theme + campaign-theme

Multiple generations of a design language

One of the promises of a design systems is to be able to deliver wholesale changes to a design language with the snap of your fingers. That’s the theory anyways. But how does it actually work in practice? The answer is creating a themeable design system that can support both legacy and future (aka 2.0! 3.0! Next Generation! NextGen! Rebrand! Facelift! Website of the future! We’ve heard it all.) design languages.

The approach is identical to supporting multiple brands, but done with the intention of being able to support the past while rolling out the future in a thoughtful and systematic way.

- core
- legacy-theme
- next-generation-theme

This architecture can be extremely powerful; it gives design system team control over how exactly to roll out aesthetic overhauls to the organization’s digital products. Consuming teams can adopt the design system using the legacy-theme with minimum disruption to the existing user experience. With the design system components in place, the design system and product teams can then coordinate on when to switch over to the next-generation-theme. We’ve found this approach to be a powerful tool for addressing teams’ understandable trepidation about changing too much, too fast when adopting the design system.

Product families

One common question I field all the time is “Can/should our software products (aka “apps”) and our marketing products (aka “websites”) share the same design system?” I’ll use my beloved invoicing/expenses/time tracking tool Harvest to demonstrate the split between marketing website and web product. Harvest has a marketing website that sells the product and provides important information:

Harvest time tracking marketing website homepage

And they have the actual web application experience where you can track time, create invoices, manage products, and so on:

Harvest time tracking web app

In my experience, the human divide between marketing and product is often just as strong as the divide we witness between designers and developers. It’s a fascinating rift to watch play out again and again in my work as a design system consultant.

It is 100% true that these digital experiences have different product needs, considerations, and audiences (most of the time). So of course these two worlds couldn’t possibly share the same design system, right? Well, not exactly.

For one, the brand is the brand and it should be consistent and cohesive throughout the organization’s digital ecosystem. Also, when you explode both marketing experiences and product experiences into their atomic bits, you’ll find they’re made of the same stuff: buttons, form fields, text, icons, and so on. These common components are the stuff of design systems, so wouldn’t it make sense to create sturdy, flexible, accessible form controls that could work well for both product and marketing experiences?

When you really break down the differences between these seemingly different experiences, it boils down to a few key areas:

  • Marketing sites use whitespace out the wazoo and need things to be jumbo sized, while product apps need things to be more compact and utilitarian
  • Marketing sites use different — and bigger — typography than product web apps.
  • Marketing and product experiences use different components: heros, touts, cards, comparison tables, and quotes for marketing websites and tables, forms, charts, and graphs for product apps.

Do those differences justify wholly separate design systems? Maybe. But I’m not convinced. Some sites use some components, others use other components. A monkey wrench and a circular saw likely aren’t needed for the same job, but they can both live alongside one another as tools in the basement. As for the aesthetic differences, we can create different themes to accommodate different typescales, sizes, and other differences between marketing website and product app experiences.

- core
- marketing
- product

Dark and Light Mode

I’ve written elsewhere about the difference between true dark mode and “inverted” (aka “knockout”) styles. Inverted/knockout tokens, which handle how certain components will render when sitting on a dark background, are often defined within a specific theme. For instance, theme-color-neutral-dark-content might be applied to default text color, but theme-color-neutral-dark-content-inverted would be applied to components that are sitting on a dark background.

Supporting dark mode officially (meaning prefers-color-scheme: dark) can be handled in a number of ways. Dark mode tokens can be co-located alongside their light-mode counterparts within a theme. Alternatively, if the light and dark modes wildly diverge, you can split them out into their own theme.

- core
- brand-theme-light
- brand-theme-dark

A flexible foundation

Design systems often get a bum rap as being a “one size fits all” solution that couldn’t possibly serve different experiences well. That perspective is of course bullshit; a design system can be as flexible as the organization needs it to be. And while this post covers flexibility from a design language perspective, there are many other aspects of flexibility involved with design systems (component variants, supporting multiple tech stacks, composition, recipes, etc). Taken together, design systems can provide a flexible foundation for many diverse products to be built on top of.