One Figma component — 3,360 Variants

One component to rule them all.

Luke Cardoni
UX Collective

--

In my work as a UX Lead at REI, I frequently contribute to our design system, Cedar. Most recently, I’ve been focusing on improving the accessibility of our form elements. It turned out that these UI components posed the perfect opportunity to start exploring Figma’s Variants feature, as each Text Input, Radio Button, and Checkbox has so many, well, variants.

Throughout the rest of this article I’ll be referring to Variants (capital V) as the Figma feature. Not to be confused with variants (lowercase v) as the actual variations of a single UI element.

Before the introduction of Variants, it was cumbersome to navigate a Figma asset library for components with multiple states or characteristics. More painful still was the challenge of creating naming conventions that were both concise and meaningful. The Variants feature eliminates this complexity entirely by allowing you to combine multiple components into one. You can also manage those states and characteristics by customizing “properties” and “values”. This makes it exponentially easier to use and maintain a given component.

Let’s look at an example from Cedar: the Checkbox

fig. 1–18 components for Checkbox in the Cedar asset library

When I began, the Checkbox component had three nested folders in the assets library, each for a different size of the element (small, medium, and large). Within those folders, there were six individual components representing the three states of the Checkbox (unchecked, checked, and indeterminate) at two interaction states (rest and disabled). In total, that’s 18 individual components for one form element alone (fig. 1).

But wait, there’s more.

As I was working to improve the design and accessibility of our Checkbox, I realized that there were additional characteristics that had not yet been accounted for in our asset library. These included interaction states for hover, focus, and pressed, spacing for form group layouts, and the background color of where the Checkbox is placed (similar to light / dark mode). This increased our count from 18 to 192 variations for Checkbox.

Adding these into Cedar introduced new complexity and even more properties to keep track of; however, it was important to include them so that anyone using the design system would be able create accessible form elements without guesswork. And thanks to the Variants feature, all 192 variations could be easily represented in a single component, simply called “Checkbox” (fig. 2). No more of this Checkbox/ Checkbox_Compact/ 1 — Rest nonsense.

fig. 2 — A single Checkbox component containing 192 Variants

I was able to apply the same approach to our Radio Button, producing a single component with 120 Variants (fig.3). Next on my list was the Text Input, which proved to be a much bigger endeavor.

fig. 3 — A single variant for Radio with 120 Variants

Whereas Checkbox and Radio, so simple in comparison, already contained nearly 200 Variants, Text Input, on the other hand, has a myriad of states, use cases, and characteristics to account for. But just how many properties does Text Input even have? How many variations could I be dealing with?

Spoiler: A lot.

Deconstructing Text Input

Text Input is one of the most ubiquitous UI elements out there. We’ve all used it at some point for entering our login details, credit card numbers, shipping and billing addresses, etc. They’re meant to be frictionless, shepherding the user through a form flow. So, where does all the complexity come from?

In Cedar, our Text Input has the following characteristics that combine in various ways based on context of use (fig. 4):

  • Input is available at 2 sizes: default and large
  • Inputs can be either required or optional
  • Text within input displays either placeholder text or a user input value
  • Icons within input: 3 slots, one left and two right
  • Helper text above and below
  • Helper links and icons
  • 4 interaction states: rest, active, disabled, and error
  • 2 supported background colors: primary and secondary
fig. 4 — Demonstrating all of the singular variable styles and properties for Text Input

At this point, some constraints and guidance needed to be put in place to limit unwanted combinations — for instance, not allowing both a helper link and icon (fig. 5), helper text above and below (fig. 6), or having all 3 icons within the field at the same time (fig. 7).

Left to right: fig. 5 — Don’t use both a helper action and a helper icon at the same time, fig. 6 — Don’t occupy both helper slots at the same time fig. 7 — Don’t use all three icon slots at the same time.

Could it be possible to document all of this in one single component? I was at least going to try…

And so began my descent into madness.

Setting up complex variants

Throughout this process, two things kept me from totally going off the rails: use of a master component and a property matrix. While a master component served as a common starting point for each of the many Variants, a property matrix helped me set up an environment to track and validate every possible permutation of the component.

Building a Master Component

A master component provides the blueprint of a complex UI element and helps manage complexity through nesting. (For additional info on nested components, I’d recommend the work of Anthony DiSpezio and Thomas Lowry.) How does this work? Let’s say there are 5 different properties we are keeping track of in a given component, and each of those properties can either be “off” or “on”. The master component displays every possible property in the “on” state. In other words, it is a representation of the component in its most complex form, regardless of whether that case would ever occur in reality.

For Text Input, the master component is made up of three nested components to which a vertical auto layout is applied (fig. 8).

  • The top component includes the field label, optional and required star, helper text that can appear above the field, and a helper link. Auto layout is applied to several levels of the nested components so that they will adapt to their content and the master frame on show / hide.
  • The middle component is made up of one component with nested layers of horizontal auto layout. The field is made up of three fixed-width icon slots, two right and one left, and a text component set to fill the container. A helper icon trails the field to the right.
  • The bottom component includes a helper text component and validation messaging component.

Here, the master component (visible on the far right in fig. 8) displays all icon slots populated, both a required and optional denotation, helper text above and below the field, a helper link and icon, and error messaging. This is the most complex state, however, you’d never see this in production. It’s simply a blueprint showing space allocations.

All of the nested components, including the master component, are prepended with .base/. Using a . or _ before the component name prevents them from being published and distributed as part of an asset library.

fig. 8 — Structural breakdown of the master component for Text Input. Nested components build from left to right, leading to the master component on the far right.

Wait — Is that a Nested Variant?!

Notice in fig. 8 that the middle component is actually a Variant in and of itself. Nesting a variant, in this case, adds to the scalability and future-proofing of this component. If, somewhere down the line, brand colors or decisions made around interaction styles were to change, they need only be adjusted at this single point within the Variant, within the master component. Nesting Variants can also be used to extend individual pieces of the master component, as long as they occupy similar slots and function in the final presentation.

fig. 9 — Converting the error messaging component into a Variant to include warning and success styles for form feedback.

While I was working on this, there were still some decisions being made elsewhere within the system for positive and warning validation styles, color, and iconography. Once those decisions are finalized, using the nested Variant approach, I plan to convert the error component into a Variant to include positive and warning feedback (fig 9). Those styles would then be available to the master component.

Now that I had a master component to work from, it was time to revisit the 7 properties and associated values for Text Input (fig 10). With those, I set up a workspace to begin building out the many permutations this component could take.

fig. 10 — Planning properties and values for the Text Input Variant

Setting up the property matrix

A property matrix requires a bit of math, but is essentially a grid system through which all possible combinations of properties and values are represented. To get started, create a table where each cell represents the values of one property. Next, duplicate the table for each value of the next property on the opposite axis. This process of duplicating and alternating axes continues until all properties are represented in the table. Doing so will ensure that a space is defined for each possible combination of values for all properties.

When preparing to do this, I would suggest strategically sorting your properties (fig. 11) so that you can work in larger and larger batches as you progress — starting with the property with the most values to the least values, or organizing by state, theme, etc. When combined as Variants, those properties and values can be re-ordered in the builder panel, so don’t stress too much.

fig. 11 — Demonstrating the creation of a property matrix using the properties and values of Text Input

For Text Input, I began with a single row table with 5 columns, one for each value in the “Icon” property. I then duplicated that row for each of the 3 values in the “Text” property. I then duplicated that set for all values in the “Required” property, and then again for “Helpers”, “Size”, “State”, and finally “Background Color”.

fig. 12 — The empty property grid that lay before me. Massive. Absolutely Massive.

I know I said that making a grid would make things easier — but in the beginning it can also be quite daunting (fig. 12). Figma was definitely running up against some performance issues. Zooming out to view the grid in its entirety would take several seconds for the screen to refresh, catch up and actually render the grid itself. At this point, I had not taken time to calculate the number of Variants that needed to be created — frankly, I was a bit afraid that doing so might discourage me. So I zoomed in, and pressed on.

Starting from the top left corner, and working horizontally, I placed an instance of the master component and adjusted the visibility of layers and styles to match the corresponding values defined by the matrix. As I continued, I was able to systematically duplicate, rename, and adjust the common setting for the corresponding property value for an entire row.

I chose to tackle this balrog of a task using the workflow I was most familiar with at the time — creating individual components and naming each layer using a naming convention for each of the property values separated by /. I think I prefer this approach, as it’s easier to select multiple layers and use the “rename” function to match and replace parts of a layer name.

My journey continued over several hours and days. As each larger, horizontal property group was finished, I was able to duplicate larger and larger batches, focusing on renaming and adjusting the single setting for the corresponding value in the property group. For example, as I completed every variation for Text Input when in the rest state, I was able to duplicate all instances and adjust them to fit the active state, then again for disabled and error.

fig. 13 — Zooming out to see all 3,360 variations of text input.

And so I droned on, cell after cell, hour after hour, burning through Spotify playlists. The hair on my neck began to stand on end as I reached the final row. I had finally filled every single space with a unique representation of every combination of values (fig. 13). I selected all of the instances, converted them all to components, and finally hit the Combine as Variants button.

I had done it. I created a single, content adaptable, fully customizable, easily maintainable component with 3,360 Variants that will serve every single possible combination of allowable settings for Text Input. A component with a number of Variants that is nearly twice as many miles travelled from the Shire to Mt. Doom (1779 miles, according to The Atlas of Middle-earth by Karen Wynn Fonstad).

Seven help compositions to arrange,
Five combinations of icon display,
Four interaction states to change,
Field text exhibits in three ways.
For information required or not,
On backgrounds both light and dark —

One Component to rule them all, One Component to align them,
One Component to contain them all, and with Variants bind them,
In the Land of Figma where design systems thrive.

The UX Collective donates US$1 for each article published on our platform. This story contributed to Bay Area Black Designers: a professional development community for Black people who are digital designers and researchers in the San Francisco Bay Area. By joining together in community, members share inspiration, connection, peer mentorship, professional development, resources, feedback, support, and resilience. Silence against systemic racism is not an option. Build the design community you believe in.

--

--