Things to consider when deciding how flexible your design system should be

Koen Vendrik

Chris Coyier, the author of CSS Tricks and Codepen, recently published “Who are design systems for?”, in which he talks about the audiences for different open-source design systems.

In the article he discusses design system customizability and the role it plays in how usable a design system is for non-company use. He concludes that design systems have different levels of customization — ranging from “not at all” to giving people broad control of how they apply styles (or “BYO Theme” as he calls it).

I want to dive into this “customization spectrum”: the different options, the audiences they cater to, what their implementation might look like, and their risks.

This is where companies build a design system mostly for themselves. A design system that is in no way customizable is very hard to use if you want to build a distinct product, since you can’t give it its own look and feel.

When we launched Shopify’s design system, Polaris, it wasn’t customizable at all. In 2018 we decided to make Polaris partly customizable and we’re still working to expand on that to make most things customizable.

We implemented the customization options because we’ve found that, while zero customizability is a great way to enforce consistency, as the company grows, the lack of flexibility has become a real pitfall.

Teams have started to develop a visual style that is intentionally different from our company-wide branding, which Polaris doesn’t allow for. The Shopify Point of Sale team, for example, started experimenting with dark mode because it made the system more usable in dark storefronts, and the Shopify Plus team started experimenting with different colors, fonts, and spacing because they wanted to visually differentiate themselves from the core product. Third-party developers currently have the same problem, as they are unable to properly express their brand.

To be able to create these products using Polaris, these teams and third parties currently have to fork the system. As a result, we’ve ended up with a bunch of different forks that all do things slightly differently.

So if you want to create a design system that can’t be customized, take into account that there may be valid reasons for customizing your system that you don’t yet foresee.

This is the other end of the spectrum. This option gives full control over the styling to the user.

On the web you see a lot of pure HTML and CSS design systems that allow for this level of customization. Since they expose raw HTML, it’s very easy to customize just about anything.

A risk this approach has is that it’s very likely that consumers of your system will integrate CSS and JS hooks into this raw markup. This will make the system very hard to maintain, since any change to the markup has a good chance of breaking one of your consumers’ implementations.

Salesforce’s Lightning Design System’s Accordion Blueprint exposes raw HTML.

Design systems that utilize frameworks (or view layers) like React are a bit different. They hide their underlying markup, making it hard to change, which makes the system much easier to maintain. By creating this clear separation between what is publicly exposed and what is only available within the system, you create a much better definition of what kind of changes introduce the risk of breaking a consumer’s implementation.

Many React based systems still do allow for a similar level of customization by allowing for class overrides. You won’t be able to freely customize the markup as you can with raw HTML, but you’re still able to apply style overrides to specific elements, and thus still have a large amount of control over how the component looks and behaves.

Material UI (A React library for Google’s Material Design) for example, allows for class overrides

This level of flexibility can be very useful and appeals to a large audience. Its ability to fully adapt to a user’s needs is very powerful.

Depending on who your design system is for, and what they can do with it, this pattern can also be risky. Giving people this level of control over styling can create the same issue that design systems are supposed to solve in the first place: having a thousand different variations of the same component or pattern.

A good example of the problem we try to solve with design systems. Taken from Andrey Okonetchnikov’s React Amsterdam talk.

This level of customization, therefore, usually isn’t ideal for a design system that’s supposed to enforce consistency since it’s too inviting for consumers to completely modify components to cater to their needs. It is, however, a great way to provide a template that is fully customizable.

Guided theme building allows your components to be themed to a degree that allows third parties to use them to create their own product, with its own look and feel, but removes the risk of people over-customizing them. Which is exactly what we will be trying to do with Polaris going forward.

A lot of design systems allow you to modify certain Sass variables to customize things like colors and fonts. Guided theme building is essentially the same thing, minus the Sass and plus some extra features.

Instead of customizing things using Sass variables, Polaris uses an overarching React component called ThemeProvider that provides a set of colors. These colors are then used to style the component(s). If you only provide a partial set of colors, Polaris uses the colors you defined to generate the rest, making it as frictionless as possible to theme your interface.

Example of a theme object that customizes the colors of our TopBar component.

This approach has two big advantages over Sass variables:

  1. Being able to only provide a partial set of colors makes it very easy (and quick) to give your UI its own look.
  2. It provides consumers with a very deliberate public customization API that is far removed from things like SCSS variables and CSS classes, which, in Polaris, are technically modifiable, but should never be modified since they’re a private API.

There are two main reasons we find this distinction between the public and private API so important. First, when people modify things using our private APIs we don’t know what would be a breaking change, which is quite problematic in a public library. Second, we want to be able to enforce consistency. Our design system is built with a very specific set of design principles in mind, and by allowing too much customization we would create the risk of consumers violating those principles.

This approach does come with its own set of problems though. One is that it relies on CSS variables and, in a system like Polaris that heavily relies on built-in Sass functions (mostly color utilities like lighten, darken and rgba), this poses a real issue. Because the theming system doesn’t know what the colors are going to be until runtime, it can’t use these Sass functions to modify colors, and we therefore need an alternative way to generate them.

This is a problem we haven’t solved yet, but we’re working on a solution so we can start expanding this API. Currently, we only allow people to customize colors on our TopBar component, but we’re working on improving the system to also allow font customizations, and perhaps even animation customizations, across all of Polaris. We’re also exploring how we can make the API less component specific, as this can lead to UI inconsistencies. We’re exploring a pattern where you wouldn’t be able to customize these properties on a per-component basis, but only on a global level.

All things considered, we feel this implementation is a good start, as it provides just the right amount of flexibility. It does a good job of enforcing the principles we really care about while still giving consumers a way to give their product its own look and feel.

If you’re interested in exploring this level of customization for your own design system make sure to check out Theme UI, which is an open-source project that provides a very similar API to the ThemeProvider system that we use for Polaris.

I hope this gives a bit more insight into what the customization spectrum looks like. As Chris mentioned in his article, it’s important that you first define who a design system is for and what people should be able to do with it. When you have decided this, and start looking at the implementation for the level of flexibility you require, keep in mind that it’s okay to do something that’s different from what’s already out there. It’s easy to create a lot of flexibility or none at all, the trick is to get it just right.

Thanks to Kaelig, Tim Layton, and Dan Rosenthal for the feedback.