Building Components For Consumption, Not Complexity (Part 1)

Building Components For Consumption, Not Complexity (Part 1)

Design systems are on the tip of every designer’s tongue, but the narrative in the industry mainly focuses on why you need a design system and its importance rather than the reality of endless maintenance and internal politics. The truth is that design teams spend years creating these systems only to find out that few people adhere to the guidelines or stay within the “guardrails.”

I’ve been fortunate with Figma to host and run workshops at various conferences across Europe that center on one very specific aspect of product design: components.

I love components! They are the start, the end, the hair-tearing-out middle, the “building blocks,” and the foundation — the everything within every great product design organization.

The reason they are so important to me is because, firstly, who doesn’t like efficiency? Second, and more importantly, they are proven to increase the time-to-delivery from design to engineering, and here comes a buzzword — they offer a route to return on investment (ROI) within design teams. This is becoming increasingly important in a tough hiring market. So what’s not to love?

Note: You may be interested in watching Matt Gottschalk’s talk from Figma’s Config 2023 conference, which was dedicated to the topic of ROI within small design teams. The talk explored how small design teams can use design systems and design operations to help designers have the right environment for them to deliver better, more impactful results.

Like in most things, a solution to this is education and communication. If you aren’t comfortable with modifications on styles, you may want to set up your components in such a way as to indicate this. For example, using emojis in layers is the quickest way to say, “Hey, please don’t edit this!” or “You can edit this!”.

Or, consider shipping out components that are named for intention. Would separating components entirely (components named for intention) work better? An Input/Error, rather than a customizable Input?

When considering the emoji name approach, here’s a set that I’ve relied on in the past:

  1. Components
    • 🚨 Deprecated
    • 🟠 Not ready
  2. Component instances
    • 🔐️ Not editable
    • ✍️ Overwritten name
  3. Layers
    • ✍️ Editable
    • 🚨 Important
  4. Component properties
    • ◈ Type
    • ✍️ Edit text
    • 🔁️ Swap instance
    • 🔘 Toggle
    • ←️ Left
    • →️ Right
    • 🖱 Interaction
    • 📈 Data
    • ↔️ Size

Flexible: Responsive Design

Ahh, our old friend, responsive design (RWD)!

“The control which designers know in the print medium, and often desire in the web medium, is simply a function of the limitation of the printed page. We should embrace the fact that the web doesn’t have the same constraints and design for this flexibility. But first, we must accept the ebb and flow of things.”

— John Allsopp, “A Dao of Web Design”

As it stands, there is no native solution within Figma to create fully responsive components. What I mean by “fully responsive” is that layout directions and contents change according to their breakpoint.

Here’s an example:

Note: It is technically possible to achieve this example now with auto layout wrapping and min/max widths on your elements, but this does not mean that you can build fully responsive components. Instead, you will likely end up in a magic numbers soup with a lengthy list of variables for your min and max widths!

With this limitation in mind, we may want to reconsider goals around responsive design within our component libraries. This may take the form of adapting their structure by introducing… more components! Do we want to be able to ship one component that changes from mobile all the way up to the desktop, or would it be easier to use, find, and customize separate components for each distinct breakpoint?

“Despite the fun sounding name, magic numbers are a bad thing. It is an old school term for ‘unnamed numerical constant,’ as in, just some numbers put into the code that are probably vital to things working correctly but are very difficult for anyone not intimately familiar with the code to understand what they are for. Magic numbers in CSS refer to values which ‘work’ under some circumstances but are frail and prone to break when those circumstances change.”

— Chris Coyier, “Magic Numbers in CSS”

Never be hesitant to create more components if there is a likelihood that adoption will increase.

Then, what does this look like within Figma? We have a few options, but first, we need to ask ourselves a few questions:

  1. Does the team design for various screen sizes/dimensions? E.g., mobile and desktop web.
  2. Does the development team build for a specific platform/screen size(s)? E.g., an iOS team.
  3. Do you build apps aligning with native style guides? E.g., Material Design.

The answers to these questions will help us determine how we should structure our components and, more importantly, what our library structures will look like.

If the answer to questions 1. (Design for various screen sizes/dimensions?) and 2. (build for a specific platform/screen sizes?) is “No,” and to 3. (Build apps aligning with native style guides?) is “Yes,” to me, this means that we should split out components into separate component library files. We don’t want to enter into a world where an iOS component is accidentally added to a web design and pushed to production! This becomes increasingly common if we share component naming conventions across different platforms.

If the answer to question 3. (“Do we build native apps, using their design guidelines?”) is “Yes,” this definitely requires a separate component library for the platform-specific styles or components. You may want to investigate an option where you have a global set of styles and components used on every platform and then a more localized set for when designing on your native platforms.

The example below, with an example mapping of library files for an iOS design project, is inspired by my Figma community file (“Simple design system structure”), which I created to help you set up your design system more easily across different platforms.

Get “Simple design system structure” [FigJam file / Luis Ouriach, CC-BY license]

If you are designing across multiple platforms in a device-agnostic manner, you can bring components a lot closer together! If you aren’t currently working with an agnostic codebase, it might be worth checking Mitosis (“Write components once, run everywhere”).

A common challenge among development teams is using the same language; while one sub-team may be using Vue, another perhaps is using React, causing redundant work and forcing you to create shared components twice. In “Create reusable components with Mitosis and Builder.io,” Alex Merced explores in detail Mitosis, a free tool developed under the MIT license. Mitosis can compile code to standard JavaScript code in addition to frameworks and libraries such as Angular, React, and Vue, allowing you to create reusable components with more ease and speed.

Using A Variant

This may look like a variant set, with a specific property for device/size/platform added. For example,

As you can imagine, as those components increase in complexity (with different states added, for example), this can become unwieldy. Combining them into the same variant is useful for a smaller system, but across larger teams, I would recommend splitting them up.

Using Sections

You could consider grouping your components into different sections for each platform/device breakpoint. The approach would be the following:

  1. Use pages within Figma libraries to organize components.
  2. Within the pages, group each breakpoint into a section. This is titled by the breakpoint.
  3. Name the component by its semantic, discoverable name.

There is a caveat here! I’m sure you’re wondering: “But couldn’t variables handle these breakpoints, removing the need for different components?” The answer, as always, is that it’s down to your specific implementation and adoption of the system.

If your designer and developer colleagues are comfortable working within the variable workflow, you may be able to consolidate them! If not, we may be better served with many components.

Additionally, the split-component approach allows you to handle components in a structurally different manner across these different sizes — something that is not currently possible with variants.

Auto Layout

Regardless of how we organize the components, responsiveness can be pushed very far with the use of auto layout at every level of our screens. Although it can be intimidating at first, the auto layout makes components work similarly to how they would be structured in HTML and CSS, moving design and engineering teams closer together.

Let’s take a simple example: a generic input field. In your main component, you’re likely to have a text label within it with the text, e.g., “Label.” Generics are useful! It means that we can swap this content to be specific to our needs at an instance level.

Now, let’s say you insert this component into your design and swap that “Label” content for a label that reads “Email address.” This is our override; so far, so good.

However, if you then decide to change your main component structurally, you put that label at risk of losing its overrides. As an example of a structural change, your original “Placeholder” now becomes a “Label” above the input field. Instinctively, this may mean creating a new text element for the label. But! Should you try this, you are losing the mapping between your original text element and the new one.

This could potentially break your existing signed-off designs. Even though this seems like it could work — layer names are a great way to preserve overrides — they are separate elements, and Figma won’t know how to transfer that override to the new element.

At this point, introducing component properties can save us from this trouble. I’d recommend adding a text component property to all of your text layers in order to try to prevent any loss of data across the design files in which we are using the component.

As I showed before, I find adding a writing emoji (✍️) to the property name is a nice way to keep our component properties panel as scannable as possible.

Content Specificity

A decision then needs to be made about how specific the default content is within the component.

And this is where we should ask ourselves a question: do we need to change this content frequently? If the answer is yes, abstracting specific textual values from components means that they can be interpreted more widely. It’s a little bit of reverse psychology, but a text layer reading “[placeholder]” would prompt a designer to change it to their local use case.

If the answer is no, we will bake the fixed value we want into the component. Going back to our input field example, we might set the default label value to be “Email address” instead of “placeholder.” Or, we could create an entirely new email address component! (This is a call we’d need to make based on anticipated/recorded usage of the component.)

Imagery / Media

When setting up a content system within Figma, a few different questions immediately pop up:

  1. How do you use specific media for specific components?
  2. How do you fix aspect ratios for media?

Within Figma, an image is essentially a fill within a shape rather than its own content type, and this impacts how we manage that media. There are two ways to do this:

  1. Using styles.
  2. Using component sets (variants).

Before we look at styles and components, though, let’s take a look at the format that all assets within Figma could take.

Practically, I would advise setting up your media assets as their own library within Figma, potentially even setting up a few libraries if you work across various products/brands with different approaches to media.

For example, the imagery your product team uses within design files and marketing materials is likely to be very different, so we would look to set these up as different Figma libraries. A designer using those assets would toggle “on” the library they need to create an asset for a specific intention, keeping the right media in the right place.

Because this media is the same as any other style or component within Figma, we can use slash naming conventions to group types of media within the names.

Domain examples:

  • Company website,
  • Product,
  • Marketing,
  • Sub brand/s.

Media types:

  • Logo,
  • Icon,
  • Illustration,
  • Image,
  • Video.

Example names, using the format:

  • Figma.com/Logo/Figma,
  • Figma.com/Icon/Variable,
  • Figma.com/Illustration/Components,
  • Figma.com/Image/Office,
  • Designsystems.com/Logo/Stripe,
  • Designsystems.com/Icon/Hamburger,
  • Designsystems.com/Illustration/Orbs,
  • Designsystems.com/Image/Modular grid.

These are split into:

  • Library: Figma.com or Designsystems.com,
  • Media type: Illustration or Logo,
  • Media name: e.g., Component libraries, Iconography.

Although I’m using images for the examples here, it works with video assets, too! This means we can move in the direction of effectively using Figma like a mini DAM (digital asset manager) and iterate fast on designs using brand-approved media assets, rather than relying on dummy content.

“A digital asset management solution is a software solution that provides a systematic approach to efficiently storing, organizing, managing, retrieving, and distributing an organization’s digital assets. DAM functionality helps many organizations create a centralized place where they can access their media assets.”

— IBM, “What is digital asset management?”

Using Fill Styles

Fill styles aren’t just for color! We can use them for images, videos, and even illustrations if we want to. It’s worth bearing in mind that because of the flexible nature of fills, you may want to consider working within fixed sizes or aspect ratios to ensure cropping is kept to a minimum.

Figma’s redline “snapping” feature lets us know when the original asset’s aspect ratio is being respected as we resize. It’s a pretty cool trick!

You can get the above example from Figma’s community:

Fixed aspect ratio images with variants” [Figma file / Luis Ouriach, CC-BY license]

For this world, though, I would advise against trying to hack Figma into setting up fully responsive images. Instead, I’d recommend working with a predefined fixed set of sizes in a component set. This may sound like a limitation, but I strongly believe that the more time we spend inside Figma, the further we get from the production environment. “Can we test this in the actual product?” is a question we should be asking ourselves frequently.

Practically, this looks like creating a component set where we set the fixed sizes along one dimension and the aspect ratio along the other. Creating a matrix like this means we can use the Component Properties panel to toggle between sizes and aspect ratios, preserving the media inside the component.

This can be used in tandem with a separate set of components specifically for images. If we combine this with Figma’s “nested instances” feature within variant components, we can “surface” all the preferred images from our component set within every instance at the aspect ratios needed!

Arrangement

This is the hardest thing to predict when we think through the usability of customizable components. The simplest example here is our old enemy: the form. Instinctively, we may create a complete form in a component library and publish it to the team. This makes sense!

The issue is that when a designer working on a particular project requires a rearrangement of that structure, we are kind of in trouble.

This problem extends to almost all component groups that require manipulation. Tables, menus, lists, forms, navigation… we will hit this wall frequently. This is where I’d like to introduce the concept of fixed vs flexible content within components, which should help to address the future problems of a world where we put the DRY (don’t repeat yourself) principles at risk.

As design system maintainers, we naturally want to keep components as composable as possible. How can this one component be used in lots of different ways without requiring us to ship an infinite number of variations? This is the central theme of the DRY principle but can be challenging in design tools because of the lack of component order management within the main components.

As a result, we often end up in a world where we build, maintain, and ship endless variations of the same component in an attempt to keep up with snowflake implementations of our core component.

“‘When should we make something a component?’ is a question I’ve been fielding for years. My strong answer: right from the start. Creating things with a component-based mindset right out the gate saves countless hours — everything is a component!”

— Brad Frost, “Design system components, recipes, and snowflakes”

For example, the form we spoke about before could be one for:

  • Logging in;
  • Registering;
  • Signing up to the newsletter;
  • Adding billing information.

These are all clearly forms that require different data points and functionality from an engineering perspective, but they will most likely share common design foundations, e.g., padding, margins, headings, labels, and input field designs. The question then becomes, “How can we reduce repetition whilst also encouraging combinatorial design?”

A concept that has been long-used in the developer world and loosely agreed upon in the design community is termed “component slots.” This approach allows the design system maintainers to ship component containers with agreed properties — sizing, padding, and styles — whilst allowing for a flexible arrangement of components inside it.

Taking our previous form examples, we can then abstract the content — login form, register form, newsletter form, and billing information form — and provide a much simpler shell component from the library. The designers using this shell (let’s call it a “form/wrapper”) will then build their forms locally and replace the slot component inside the shell with this new custom main component.

This is best explained visually:

Does this custom component need to live in multiple files? If yes, we move it to the next level up, either team-level libraries or global, if working on a smaller system. If not, we can comfortably keep that component local to the specific Figma file on a page (I like to call it “❖ Components”).

Important: For this premise to really work, we must employ auto layout at every level, with no exceptions!

Conclusion

That was a lot to process (over five thousand words, actually), and I think it‘s time for us to stop staring at the computer screen and take a little break before walking through the next set of principles.

Go grab a drink or take some rest, then meet me in Part 2, where you will learn even more about the adoptable, indexable, logical, and specific components.