This article is a sponsored by Penpot
Since Brad Frost popularized the use of design systems in digital design way back in 2013, theyâve become an invaluable resource for organizations â and even individuals â that want to craft reusable design patterns that look and feel consistent.
But Brad didnât just popularize design systems; he also gave us a framework for structuring them, and while we donât have to follow that framework exactly (most people adapt it to their needs), a particularly important part of most design systems is the variants, which are variations of components. Component variants allow for the design of components that are the same as other components, but different, so that theyâre understood by users immediately, yet provide clarity for a unique context.
This makes component variants just as important as the components themselves. They ensure that we arenât creating too many components that have to be individually managed, even if theyâre only mildly different from other components, and since component variants are grouped together, they also ensure organization and visual consistency.
And now we can use them in Penpot, the web-based, open-source design tool where design is expressed as code. In this article, youâll learn about variants, their place in design systems, and how to use them effectively in Penpot.
Step 1: Get Your Design Tokens In OrderFor the most part, what separates one variant from another is the design tokens that it uses. But what is a design token exactly?
Imagine a brand color, letâs say a color value equal to hsl(270 100 42) in CSS. We save it as a âdesign tokenâ called color.brand.default so that we can reuse it more easily without having to remember the more cumbersome hsl(270 100 42).
From there, we might also create a second design token called background.button.primary.default and set it to color.brand.default, thereby making them equal to the same color, but with different names to establish semantic separation between the two. This referencing the value of one token from another token is often called an âaliasâ.
This setup gives us the flexibility to change the value of the color document-wide, change the color used in the component (maybe by switching to a different token alias), or create a variant of the component that uses a different color. Ultimately, the goal is to be able to make changes in many places at once rather than one-by-one, mostly by editing the design token values rather than the design itself, at specific scopes rather than limiting ourselves to all-or-nothing changes. This also enables us to scale our design system without constraints.
With that in mind, hereâs a rough idea of just a few color-related design tokens for a primary button with hover and disabled states:
| Token name | Token value |
|---|---|
color.brand.default |
hsl(270 100 42) |
color.brand.lighter |
hsl(270 100 52) |
color.brand.lightest |
hsl(270 100 95) |
color.brand.muted |
hsl(270 5 50) |
background.button.primary.default |
{color.brand.default} |
background.button.primary.hover |
{color.brand.lighter} |
background.button.primary.disabled |
{color.brand.muted} |
text.button.primary.default |
{color.brand.lightest} |
text.button.primary.hover |
{color.brand.lightest} |
text.button.primary.disabled |
{color.brand.lightest} |
To create a color token in Penpot, switch to the âTokensâ tab in the left panel, click on the plus (+) icon next to âColorâ, then specify the name, value, and optional description.
For example:
- Name:
color.brand.default, - Value:
hsl(270 100 42)(thereâs a color picker if you need it).

Itâs pretty much the same process for other types of design tokens.
Donât worry, Iâm not going to walk you through every design token, but I will show you how to create a design token alias. Simply repeat the steps above, but for the value, notice how Iâve just referenced another color token (make sure to include the curly braces):
- Name:
background.button.primary.default, - Value:
{color.brand.default}

Now, if the value of the color changes, so will the background of the buttons. But also, if we want to decouple the color from the buttons, all we need to do is reference a different color token or value. MikoĹaj Dobrucki goes into a lot more detail in another Smashing article, but itâs worth noting here that Penpot design tokens are platform-agnostic. They follow the standardized W3C DTCG format, which means that theyâre compatible with other tools and easily export to all platforms, including web, iOS, and Android.
In the next couple of steps, weâll create a button component and its variants while plugging different design tokens into different variants. Youâll see why doing this is so useful and how using design tokens in variants benefits design systems overall.
Step 2: Create The ComponentYouâll need to create whatâs called a âmainâ component, which is the one that youâll update as needed going forward. Other components â the ones that youâll actually insert into your designs â will be copies (or âinstancesâ) of the main component, which is sort of the point, right? Update once, and the changes reflect everywhere.
Hereâs one I made earlier, minus the colors:

To apply a design token, make sure that youâre on the âTokensâ tab and have the relevant layer selected, then select the design token that you want to apply to it:

It doesnât matter which variant you create first, but youâll probably want to go with the default one as a starting point, as Iâve done. Either way, to turn this button into a main component, select the button object via the canvas (or âLayersâ tab), right-click on it, then choose the âCreate componentâ option from the context menu (or just press Ctrl / â + K after selecting it).

Remember to name the component as well. You can do that by double-clicking on the name (also via the canvas or âLayersâ tab).

To create a variant, select the main component and either hit the Ctrl / â + K keyboard shortcut, or click on the icon that reveals the âCreate variantâ tooltip (located in the âDesignâ tab in the right panel).

Next, while the variant is still selected, make the necessary design changes via the âDesignâ tab. Or, if you want to swap design tokens out for other design tokens, you can do that in the same way that you applied them to begin with, via the âTokensâ tab. Rinse and repeat until you have all of your variants on the canvas designed:

After that, as you mightâve guessed, youâll want to name your variants. But avoid doing this via the âLayersâ panel. Instead, select a variant and replace âProperty 1â with a label that describes the differentiating property of each variant. Since my button variants in this example represent different states of the same button, Iâve named this âStateâ. This applies to all of the variants, so you only need to do this once.
Next to the property name, youâll see âValue 1â or something similar. Edit that for each variant, for example, the name of the state. In my case, Iâve named them âDefaultâ, âHoverâ, and âDisabledâ.

And yes, you can add more properties to a component. To do this, click on the nearby plus (+) icon. Iâll talk more about component variants at scale in a minute, though.

To see the component in action, switch to the âAssetsâ tab (located in the left panel) and drag the component onto the canvas to initialize one instance of it. Again, remember to choose the correct property value from the âDesignâ tab:

If you already have a Penpot design system, combining multiple components into one component with variants is not only easy and error-proof, but you might be good to go already if youâre using a robust property naming system that uses forward slashes (/). Penpot has put together a very straightforward guide, but the diagram below sums it up pretty well:

Design tokens, components, and component variants â the triple-threat of design systems â work together, not just to create powerful yet flexible design systems, but sustainable design systems that scale. This is easier to accomplish when thinking ahead, starting with design tokens that separate the âwhatâ from the âwhat forâ using token aliases, despite how verbose that might seem at first.
For example, I used color.brand.lightest for the text color of every variant, but instead of plugging that color token in directly, I created aliases such as text.button.primary.default. This means that I can change the text color of any variant later without having to dive into the actual variant on the canvas, or force a change to color.brand.lightest that might impact a bunch of other components.
Because remember, while the component and its variants give us reusability of the button, the color tokens give us reusability of the colors, which might be used in dozens, if not hundreds, of other components. A design system is like a living, breathing ecosystem, where some parts of it are connected, some parts of it arenât connected, and some parts of it are or arenât connected but might have to be later, and we need to be ready for that.
The good news is that Penpot makes all of this pretty easy to manage as long as you do a little planning beforehand.
Consider the following:
- The design tokens that youâll reuse (e.g., colors, font sizes, and so on),
- Where design token aliases will be reused (e.g., buttons, headings, and so on),
- Organizing the design tokens into sets,
- Organizing the sets into themes,
- Organizing the themes into groups,
- The different components that youâll need, and
- The different variants and variant properties that youâll need for each component.
Even the buttons that I designed here today can be scaled far beyond what Iâve already mocked up. Think of all the possible variants that might come up, such as a secondary button color, a tertiary color, a confirmation color, a warning color, a cancelled color, different colors for light and dark mode, not to mention more properties for more states, such as active and focus states. What if we want a whole matrix of variants, like where buttons in a disabled state can be hovered and where all buttons can be focused upon? Or where some buttons have icons instead of text labels, or both?
Designs can get very complicated, but once youâve organized them into design tokens, components, and component variants in Penpot, theyâll actually feel quite simple, especially once youâre able to see them on the canvas, and even more so once youâve made a significant change in just a few seconds without breaking anything.
ConclusionThis is how we make component variants work at scale. We get the benefits of reusability while keeping the flexibility to fork any aspect of our design system, big or small, without breaking out of it. And design tools like Penpot make it possible to not only establish a design system, but also express its design tokens and styles as code.