Crafting Component Libraries: The Elements
Component libraries enable interfaces to be defined through individual building blocks that can be composed together to form a greater whole. The net result is an adaptable visual language that unlocks greater consistency, efficiency, and scalability. In this article, I’ll step through my process for crafting the foundational elements that make up a component library based on design fundamentals that can grow and evolve to fit the needs of any interface.
To demonstrate my process, we’ll go through the workflow I followed when creating a component library for the project Biased by Design. While this project is relatively small, the process I’ll cover can be followed to build component libraries for projects of any size. I’ve used Figma to create the component library that we’ll cover in this article and I’ll build upon some basic concepts. If you’re not already familiar with Figma, I recommend this playlist for a quick tour of the key concepts and features.
Typography & Scale
Let’s begin by establishing the two backbone elements of any component library: typography and modular scale. Written language plays a foundational role in design — it’s what makes up the majority of the content we consume and is a vital mechanism for communication. For this reason, beginning with type makes a lot of sense when starting any component library. A modular scale is also a critical part of the library because it informs the size and spatial relationships between all elements in the library.
Typography
I begin every component library with type since the values derived from this foundation element directly affect many other items in the library. The typeface and sizing we choose for the body text is the lowest common denominator — it’s the base element that all others have in common by default. Therefore, it’s important to begin designing our library with this in mind.
The first step is to choose a suitable typeface (preferable one with several weights) and apply it to a block of text. The default font size on the web is 16px (1em), and that’s usually a great baseline to start with for body text. Next up is determining the default line-height, which will vary based on the typeface you’ve chosen for the body text. You can experiment with different line-heights until you find a value that provides enough breathing room between lines of text without losing the continuity between rows.
The remaining items in your typography scale will originate from this base size plus a scale multiplier, which we’ll define next.
Scale
Once I select the body text, it’s time to define a modular scale. The scale we use will be the logic behind the values throughout the library, so it’s helpful to establish this early on. We start by multiplying the value of the base font text by a scale ratio (1.333, 1.25, 1.5
) to get the next value up and repeat this process to get additional values. For example: if my base font size is 16px and the scale ratio is 1.5
, I multiply 16 × 1.5
to get 24px
. I could then multiply 24 × 1.5
to get 36px
and so on. You can also get smaller values that are lower than your base (16px
) by dividing the base font-size by scale unit (e.g. 16px ÷ 1.5 = 10.667px
). I continue this process until there are 8-10 sizes from the scale (including the base value).
No need to do all the math here manually — there’s a handy tool to quickly test and determine modular scale values called Modular Scale.
The scale you choose will determine how many units you have available, e.g. the higher the scale, the greater the difference between values. This will affect how much flexibility you have, so keep this in mind when determining what scale you use.
Once we’ve established a modular scale, let’s go back and define the remaining items in our typography scale. Don’t forget to also set a line-height value for these headings, which will vary depending on the typeface. I’ll go with 1.3x the font size (rounded to nearest whole pixel) for headings. Once I’ve established the remaining items in the type scale, it’s time to save these values for reuse later on by saving them as text styles.
Keep in mind that we’re just defining some foundational type styles. These styles will and should evolve as you begin working through your designs.
Grid System
Continuing with defining the foundational elements of the library, it’s time to establish a grid system. Grid systems are invaluable for not only establishing rhythm and alignment in a design, but they are great for ensuring there’s consistency. To define your grid system, create a new frame set to a large size, e.g. 1920×1080px
. Next, use the grid settings to create a grid that’s 12 columns with a gutter value that comes from the modular scale (16, 24, etc).
A 12 column grid system provides lots of flexibility in how you lay components out later on because 12 is evenly divisible by 2, 3, 4, and 6, but you can experiment with other columns counts as well. The column count you choose will depend on the intricacy and scale of the design, with additional columns providing more layout control and flexibility. Once the grid system is established, it’s a good practice to save it as a grid style for reuse throughout the designs.
Colors
Colors are another foundational design element that will be defined early on. I like to start by defining the 1-4 primary colors, followed by the tints and shades of each color (if necessary). Tints and shades are useful for the more subtle color adjustments while staying consistent with the base color value. My favorite color format for this purpose is HSL because tints and shades can be easily defined by simply adjusting the ‘L’ or lightness value of the base color. Using this method, I will create the tint and shade variations of each primary color and include adjustment value differences in the naming of the color upon saving it in my design tools.
I tend to keep my color systems on the lean side, i.e. only define the colors (plus tints and/or shades) that I’ll need. This not only keeps things light and easy to manage but also helps to ensure more color consistency throughout the design.
Once the colors are defined, it’s time to save those as reusable styles so they can be referenced throughout the component library. The first step in saving the colors is to name each color appropriately (appending the difference in the HSL ‘lightness’ value to the end of the base color name). Once this is done, I follow the same steps for saving the colors as styles as I did with the text. Lastly, I double-check that the colors and their names are accurate via the Color Styles panel on the right-hand side of my Figma window.
Color Contrast
It’s never too early to check your colors for proper color contrast, specifically in regards to text. Sufficient color contrast not only makes an interface more accessible but improves readability for all users. It’s good to begin checking color contrast as soon as you have your typography and colors defined, and there are plenty of great tools to help with this. The tool I like to use is called Stark, which provides a suite of integrated accessibility tools and can easily be installed as a Figma plugin. You can quickly check the color contrast of a text and background layer by first selecting them and then running the ‘Color Contrast’ test. The goal is to make sure the contrast between the text and background is greater than or equal to 4.5:1 for body text and 3:1 for large text.
Iconography and Media
The last foundational design elements to define are iconography and images. Iconography usually plays a foundational role in any component library. Many if not most components will incorporate icons, and therefore establishing a flexible system for them is critical to the library. Related to iconography are media items like images and videos, which provide rich content and interaction for users but also play a foundational role in any design language. Therefore, we must define them with flexibility in mind.
Icons
I like to start by defining what icon sizes will be supported, defining them in t-shirt sizing (small, medium, large). Defining sizing according to t-shirt sizing limits the number of items by default, but this is usually exactly what you want: a defined set of values that work well in the context of the system overall, not every option imaginable with naming that has no relevance to the actual design (e.g. ‘size-1’, ‘size-2’, ‘size-3’, etc.).
Much like we did with the body text, it’s helpful to start with the lowest common denominator when defining icon sizing. Once again returning to our modular scale, select a unit from the scale that works well for icon size in combination with the body text. This will require some experimentation, so having a separate page that works like a sketch pad to experiment while you create your library is helpful. Once you’ve landed on a suitable base icon size, the next step is to create the additional sizes. This is a great use case for the variants feature in Figma, which allows you to group and organize similar components into a single container.
Media
Media items like images and videos are next up. The grid system we established earlier will be our guide as we define the various image sizes needed. Once again using the t-shirt sizing constraint, I will typically create a base, small, medium, and large image variation based on the widths that are derived from the grids, and the heights being dictated by a consistent aspect ratio (e.g. 16:9 of 1920 is 1080, etc.). I also make sure to create a ‘full-bleed’ image variation for circumstances where I want the image to take up the full width of the design, as well as smaller ‘thumbnail’ variations that might not be the same aspect ratio as content images.
We’ve gone through a step-by-step breakdown of my process for building UI component libraries based on design fundamentals that can grow and evolve to fit the needs of any interface. These foundational elements that were established will form the foundation for the components and patterns that come in the future. Next up, we’ll dive into how to define those reusable components using these foundational design elements. Stay tuned!