Creating Components For Mature Design Systems

Felix Ng
Felix Ng

Product Designer - Meta

Creating Components For Mature Design Systems

Illustration by Lolita Calistru for this article

L

ike many designers, I’ve worked with design systems of many sizes. I started out creating basic UI kits as the sole designer at seed-stage startups. From there, I transitioned to scaling systems at late-stage startups with small design teams. And now at Meta, I’m on a dedicated design system team of 25+ designers and engineers, building and scaling our centralized system for 100+ design teams.

Four (not-so-easy) steps

All systems roughly follow a similar procedure when building components:

  • Context — Gather the full use case and details of this component or pattern.
  • Define — What does it do, and what doesn’t it do?
  • Audit — Review your system and ensure this component has a place.
  • Build — Design, code, document, and ship!

So how does the component creation process differ as you scale towards a larger system?

The steps themselves don’t change too much — but the prioritization does.

Adjust your prioritization as you scale

Early stage system: focused on growth, with the Build step taking 90% of priorities. Medium stage system: focused on growth and maintenance. Build step takes up 80% of the priorities. Mature system: focused on governance, maintenance, and support. Build step takes up 50% of the priorities. Context, Define, and Audit are all at 80% or more.
Breakdown of resources for each step, based on my experience.


In smaller design systems, steps 1–3 can be fairly straightforward. There’s usually little overlap of components and you can often go straight towards the building, which makes perfect sense — these systems are in growth mode, and component gaps are often the largest challenge.

In mature systems, each new pattern or component results in increased maintenance, support, and documentation. Because of this, steps 1–3 are critical and can be more time-consuming than actually designing components.

Minimizing ambiguity through component definitions

One of the biggest challenges of working within a mature design system is governance. The more components in your system, the higher chance of component misuse and confusion. One way to mitigate this is by having clear component definitions.

One of the first components I worked on at Meta is a chip-like component, similar to Material Design’s Chip. The initial task was to build it out for mobile to maintain parity with our existing web chip component. It sounds incredibly straightforward — it’s a chip, a very established pattern. Our original definition of our chip was:

  • A chip can be used on its own or to represent data within a search typeahead component.

Step 1: context

We gathered input from all of our partners and sought to see any use cases in which this chip could be utilized for mobile. Immediately, we found a vast number of valid use cases: labels, tags, filters, shortcut links, buttons, and user entities.

Showcase of different variants of the Chip component
Showcase of different variants of the Chip component

To reduce future friction, we aimed to build this chip in a way that could eventually support all of them.

Step 2: define

From our findings, it was a simple modification from the initial definition: We only needed to remove the scope limitation and note that chips should always be displayed in a group of chips.

  • A chip represents some form of data. They should never be used in explicitly standalone cases, and should often be represented within a group of chips.

Step 3: audit

The audit process is the most important step when working with larger design systems. Some of the questions we ask ourselves are:

  • Will all of our design system users find value in this component?
  • Are there any visually/functionally similar components that already exist?
  • Can these use cases be supported with existing components?
  • Will this component impact any existing components?

Through our audit, we immediately found some large issues with question #2. Our system already has a visually similar Pill component.

Side-by-side comparison of visual similarities between Chip and Pill components
Side-by-side comparison of visual similarities between Chip and Pill components

Most importantly, there was some overlap with the definitions:

  • A chip represents some form of data. They should never be used in explicitly standalone cases and should often be represented within a group of chips.
  • A pill is used to display data — often to indicate a status, to show an aggregated value, or to highlight a single option.

In addition, we found that some teams were already misusing pills as non-status labels. How might we differentiate the chip from pills? Is the chip differentiated enough to account for some use cases?

Step 3.5: redefine

Through numerous in-depth conversations from both design and engineering perspectives, we settled on the distinction being user-set vs. system-set. We also wanted to highlight the standalone nature of pills. Our new definitions were:

  • A chip represents pieces of data that the system recognizes as user input. They should be represented within a group of chips and never be used in explicitly standalone cases
  • A pill represents data that is system-set and often represents a status, an aggregated value, or to highlight a single option. They are standalone and should not be grouped with other pills.

These revised definitions prevent us from veering too far from the initial scope of the chip and provide clear guidance for Designers on which component to use.

Step 4: build

With component definitions out of the way, we can finally get to the fun part — building.

With Figma variants, we can easily add properties to define the attributes of our components. Paired with Nested components, many designers already use a “building-block” approach when building Figma components.

Here’s an excellent Figma community example from Stéphane using this approach with the Material Design text field:

Material Design Text Field” by Stéphane is licensed under CC BY 4.0.

Each variant of the Text Field component contains an instance of the building block component _Structure. Any changes made to this building block will apply to all variants.

Tip: Prefix your components with an underscore ‘_’ or a period ‘.’ and they won’t be published to your libraries. We don’t want designers to use these building blocks in their designs.

I am a huge advocate of this approach, especially with larger design systems. It allows design system maintainers to apply changes at a massive scale.

Example:

Imagine you need to change the font of the above text fields from Roboto to Google Sans. If you built these with Figma text styles, then swapping the font is simple and straightforward.

However, different fonts may require layout changes due to differences in x-height or character widths. With a building block approach, you can adjust margins and spacing in one place. Any changes made will seamlessly populate to all variants of the text field.

Conclusion

Regardless of size, every design system follows a similar process of collecting context, defining, auditing, and building. As you scale your system, consider your time investment in each of these four steps:

  • Would it be beneficial to conduct more audits?
  • Are designers or engineers confused about which component to use?
  • And most importantly — do you really need another component?

Every system has a different approach, and I’m always discovering new ways to optimize and improve design systems. If you have your own tips or experiences you’d like to share, please reach out!