Building a Design System Library
By: Jeremy D.
Tags: Tips And Tricks, Best Practices, Lyft Product Language, Figma, Design Systems

Building a Design System Library

Note: This article was originally published here

At the end of 2019, I helped move all of Lyft’s design system from Sketch to Figma. Ironically, this was one of the last projects I worked on prior to leaving Airbnb as well. What I learned from these two experiences helped me recognize some fundamental things that everyone should consider when designing a shared library in Figma, the design tool of choice at this time.

In this blog post, I want to discuss some best practices that we followed to create the multiple Lyft Product Language attributes and component libraries. And at the end, I cover some helpful tips and tricks that allow us to efficiently maintain them today.

System-ception: a system within a system

When we started to tackle creating our system in Figma, we took a step back to re-evaluate what we’d done in Sketch, questioning the decisions we’d made about layout, visual hierarchy, and naming convention, to name a few. We realized that in order to create a strong foundation in these new libraries, we had to take the time to create a system within a system that would apply to every element in the file. In the end, we essentially built in a certain level of quality and consistency into the library which allowed us to scale the number of library maintainers.

Carousel Page Indicator main and internal components. Note the file canvas has a #F4F4FA background fill.

We set up each component page with visual hierarchy to help distinguish between the main components and the internal components that make them up. All the main components are placed in frames that have a 4px inner outline without a background fill. These Frames act as the component sticker sheet.

The internal components are placed in a Frame with a white background. Displaying all of the internal components allows the maintainer a snapshot of the options that can be used to reconfigure the main component. The goal of these internal components is to allow a user to reconfigure a main component into the variation they need and to promote shared internal components, making it more efficient to maintain.

A system of padding

In each Frame, there is specific padding used. The benefit of considering a system of padding is that it creates a visual consistency between all the elements on the canvas, from page to page, and library to library.

Our component libraries utilize these paddings:

60px: Internal Frame padding (top, right, bottom, and left)

Internal Frame padding (top, right, bottom, and left) 40px: Between Frame title to 1st main component

Between Frame title to 1st main component 24px: Between Frame title to Subtitle (optional)

Between Frame title to Subtitle (optional) 24px: Vertically between main components

Vertically between main components 40px: Horizontally between main components

Layers reordered visually from top to bottom

By default, when adding layers in Figma, the new layer is created above the current one in the Layers panel. A bookkeeping and tedious task is reordering the layers from top-to-bottom, left-to-right, based on the visual hierarchy of the Frame.

When managing a large number of main components and internal components, mapping the layers in this way allows the maintainer to quickly navigate a sometimes chaotic canvas.

Naming convention applied to the focus compact button

After facilitating a quick survey in Slack with users, we discovered that the majority of them use the Assets Panel to search for and discover components. How components are named is important to this workflow.

We created and applied this naming structure to all of our components:

File name / Page name / Frame name / Component name (variation, style, type & state)

The file name is the name of the specific library and what users will need to enable in the Assets panel in order to use the components in their mockup files (ie. Core UI Components (Android/iOS), Core UI Components (Web), Core UI Color, etc).

The page name is the name of the component (ie. Carousel Page Indicator, Button, List Item, etc).

The frame name is the top-level grouping of the component, usually the size (ie. Buttons have 4 sizes: Focus, Focus Compact, Drive, and Drive Compact).

The component name is the name of the main component and its variations. Due to the number of variations, using the backslash ( / ) allows them to be categorized into other defining categories, like states. Even though this essentially places them into more “folders”, this helps bucket similar variations to be easily swapped in the Instance dropdown and discovered in the Assets Panel folder structure.

Visual of the folder structure in the Instance dropdown

More flexible components

Creating atomic elements is the foundation of a design system. The Lyft Product Language is broken into atomic elements and then used to create specific components. These internal components help provide consistency across a large team (ie buttons) as well as cut down on maintenance, especially when these internal components are shared across multiple components (ie text field, text area, dropdown, etc).

List item component consists of 3 areas: Start, Middle, and End

The best example of how internal components make up a component can be seen in our List item. This component is set up with areas that can be reconfigured to create a number of variations. Under each area, there are different internal components you can choose from. Plus, by structuring them at the same size, only those choices are allowed for each area (ie there are only 4 internal components allowed for the Drive Default’s Start area).

Internal components allowed for the Start area

Internal components allowed for the Middle area

Internal components allowed for the End area

For a complex component like List Item, internal components are valuable to allow the flexibility of reconfiguring a component to fit various use cases.

Let’s talk about responsiveness

There are other things to consider when creating flexible, shared components that respond in the exact way you intend. A lot of these options can be defined in the Design panel within the Figma interface.

The design panel in Figma: source

Creating responsive components is important to promote adoption across all users since most apps support various screen sizes and platforms. A way to achieve this is by defining how your component’s layers resize accordingly based on what you set for the Horizontal and Vertical constraints.

Horizontal constraints: source

Horizontal constraints

Left pins the layer to the left side of the frame.

pins the layer to the left side of the frame. Right pins the layer to the right side of the frame.

pins the layer to the right side of the frame. Left and right allows the layer to grow or shrink along the x-axis.

allows the layer to grow or shrink along the x-axis. Center allows the layer to float towards the horizontal center of the frame.

allows the layer to float towards the horizontal center of the frame. Scale allows the layer to grow or shrink as a percentage of the Frame’s dimensions.

An example where it’s a good idea to set horizontal constraints is when resizing a List item component where you want an icon stay in the same location while allowing the text layer to grow or shrink.

To do this, simply set your Start layer to a left horizontal constraint and your Middle layer to a left and right horizontal constraint.

Example of horizontal constraints settings for a List Item component.

Vertical constraints: source

Vertical constraints

Top pins the layer to the top of the frame.

pins the layer to the top of the frame. Bottom pins the layer to the bottom of the frame.

pins the layer to the bottom of the frame. Top and bottom allows the layer to grow or shrink along the y-axis

allows the layer to grow or shrink along the y-axis Center allows the layer to float towards the vertical center of the frame.

allows the layer to float towards the vertical center of the frame. Scale allows the layer to grow or shrink as a percentage of the Frame’s dimensions.

An example where it’s a good idea to set vertical constraints is when resizing a Text area component where you want the message layer to pin to the bottom while the text area outline to grow or shrink.

To do this, simply set your Message layer to a bottom vertical constraint and your Text frame layer to a top and bottom vertical constraint.

Example of vertical constraints settings for a Text Area component

#8 bullet — type resizing options: Auto Width, Auto Height, and Fixed Size: source

In a similar way of constraints, there are some controls you can set on your type layers to accommodate shrinking or growing based on its content. This works in conjunction with the constraints you set.

Auto Width

This option allows your text layer’s width to grow based on your content. I typically set a text layer’s width to auto width when wanting my layer to grow based on my content. By setting the horizontal constraints to the left along with the auto width option, my text layer will resize accordingly.

Example of auto width text settings for a Text Area component

Auto Height

This option allows your text layer’s height to grow based on your content. The original text layer’s width will dictate when your content wraps to a new line. I typically set a text layer’s height to auto height when wanting my content to wrap to a second line within a component. By setting the horizontal constraints to left and right along with the auto height option, my text layer will wrap the way I intend it to.

Example of auto height text settings for a Text Area component

Fixed Size

This option sets your text layer’s width and height, no matter the content. The text layer’s set width will dictate when your content wraps to a new line and will extend past the text layer’s set height without being clipped.