UI Library & Styleguide

Translating a design language into a UI Library and interactive styleguide.

A snapshot of our React Storybook-powered interactive styleguide

Whether the craft in question is design or code, one of the trickiest parts of scaling a team is adhering to a consistent style. When I joined Button in August 2016, our Design Team had just kicked off the creation of Button’s Design Language System (DLS). The resulting core styles and components had the potential to bring an unprecedented level of consistency and visual polish to our designs. This was especially true for the web, which lacks the clear interface guidelines of native mobile apps.

As Button’s resident UX Engineer, I took the lead on enabling our frontend engineers to easily build UIs that followed our Design Language System. In this post, I’ll explain how I translated our DLS into a collection of CSS classes and React components shared across all of our frontend repos, complete with an interactive styleguide powered by React Storybook.

Getting Started

In creating a UI Library, I saw a valuable opportunity to solve many of the styling challenges the Web Team encountered on a daily basis. These included:

  • Global scoping: CSS’s global scoping makes it hard to predict which areas of your application may be affected when adding new rules. As a result, many teams resort to lengthy naming conventions that can be hard for code readers to interpret.
  • Overriding defaults: Before the introduction of the UI Library, we relied primarily on Foundation to style our web UIs. Like most CSS frameworks, Foundation applies default styling to each HTML element based on tag type. This is convenient for building very simple UIs, but often requires highly specific CSS and encourages the use of semantically incorrect HTML.
  • Sharing styles across repos: Button maintains multiple web applications across multiple repos. An independently-maintained UI Library would make it easy to share styles across those applications.

To address these issues, we established the following conventions for the library:

  • Locally scoped CSS: On the suggestion of Daniel, one of my fellow frontend engineers, I experimented with using CSS Modules to power the charts in our Partner Dashboard. Our team found it liberating to be able to write simple, straightforward class names with no fear of conflicting CSS, and decided to use them throughout the library.*
  • No default styles: The library would not style any HTML tags by default. Instead, all of our core styles would be applied using classes, and our UI components would provide their own styling.
  • Keep it “open-sourceable”: Anything that went into the library should theoretically be open-sourceable, and therefore shareable across repos. In other words, the library would consist only of presentational components and classes. That meant no assumptions about the use of particular libraries (no depending on React-Router’s DOM library when building a <Link> component, for example) and no Button-specific content.

*We did consider using inline styles in React, but their lack of support for features such as pseudoselectors, our large existing CSS codebase, and our designers’ fluency in CSS influenced our decision to opt for CSS Modules. Over a year later, we’re very happy with them.

Building the Library

Our current Design Language System is broken into two major categories: Core Styles, such as typography, colors, and spacing, and UI Components, which are built on top of those core styles. These concepts mapped naturally to implementation as CSS classes and React Components. In the sections that follow, I’ll describe how we designed each for ease of maintenance and use.

Core Styles

Even the most elemental core styles, such as type, often involve a number of CSS rules. To keep our CSS as modular as possible, each main property is represented as a variable, whereas helper classes intended for composition only are prefixed with an underscore (e.g. _fontFamily). The remaining classes are public, and may either be composed in CSS or applied directly in JSX.

Here’s a sample of variables and classes from our typography.scss file:

SCSS variables for our typography styles
A selection of our typography helpers and the public display1 class

With these variables and classes in place, adding the correct spacing and typography to a UI is simply be a matter of importing the correct helper classes and using them directly in JSX. Here’s a simple example:

Note that I’ve used an <h1>and a <p> above, but those tags could easily be swapped out depending on their purpose in the application. Applying helper classes directly to JSX creates more verbose components compared to semantic class names (e.g. applying a header class here that composes display1 and marginBottomXs, vs. composing those classes in a .header class). However, one of the benefits of using React components is the ability to easily scan the render function and glean an understanding of the component’s presentation. This approach capitalizes on that benefit, making it easy for both the developer and code reviewer to ensure that the HTML and styling are correct when implementing new components.

UI Components

To translate each UI component into React, I began by identifying the properties that would vary across individual instances of a component. These would become the props of the React component. Take the <TransientAlert>component, which floats down from the top of the screen to notify the user that a change has occurred after they initiate an action.

This component’s props include:

  • Contents: The string to display inside the alert
  • Timeout: The period of time to display the alert before it dismisses
  • Status: Either success, warning, or danger

Most of the time, each prop can be translated directly into a PropType, but those shared across components merit special attention. A great example of this is our Status system, which uses consistent colors and icons to indicate a success, warning, or danger state. We use Immutable.js throughout our frontend codebases, so I encoded our Status system as an Immutable Record, along with a custom PropType and helper functions for use across the UI Library.

An example of shared helpers

That allows us to translate a simplified version of the component’s PropTypes and render function as follows:

The full version is a bit more complicated, but the core concept for our components is the same.

From there, the next step is to implement the component itself based on the design in the Sketch file. As you can see below, the code builds heavily upon our core styles, but also includes one-off rules to implement the styles unique to the component. (A tool I’ve found helpful for building components exactly to spec is Zeplin, which makes it easy to inspect designs without a Sketch license).

A simplified version of the TransientAlert render function
A snippet from TransientAlert.scss. Note the use of color variables–no custom hex or rgba necessary!

With that code in place, using a TransientAlert is as easy as the following:

<TransientAlert status={ActionStatus.SUCCESS} timeoutMs={1000}>
Your changes have been saved.
</TransientAlert>

A great component should be flexible enough to handle any of its intended uses, but restrictive enough to encourage correct usage. In the example above, one tradeoff we’ve made is allowing the callsite to pass variable children to the alert. This makes it possible to embed links in the text, but also makes it possible to pass other nodes that wouldn’t belong, such as images.

Interactive Styleguide

The final piece of the puzzle is our React Storybook-powered interactive styleguide, which includes the following helpful features:

Introduction
When a user first opens the Styleguide, they’re greeted by an introduction to our UI Library and how to use it. They can also choose to read more about its structure and conventions, such as the Status system.

Component Overview
Users may also select a component from the left-hand navigation. The Overview page that appears describes how to use the component correctly and displays representative examples. For interactive components such as the Tabs below, it uses a wrapper that allows the user to try out the interaction.

Component Playground
Finally, each component also provides a playground powered by a few Storybook plugins that allows the user to try out different combinations of props, viewport sizes, and background colors.

By clicking Show Info in the Playground, users can view the story’s source and the component’s PropTypes. This option makes it possible to use the UI Library without having to reference components’ source code, which means less time looking up props and more time developing.

Impact

Investing the time to build a UI Library isn’t easy, especially at a young startup with only a handful of frontend engineers and designers. However, the tradeoff was well worth it. Some of the benefits we’ve observed include:

  • Better design/eng communication: 1:1 versions of each core style and component in Sketch and React ensure that our designers and engineers are speaking the same language.
  • Onboarding: The introduction section of the Styleguide makes it easy for new contributors to learn about our Design Language System and get started building new components.
  • Out-of-the-box styling: By abstracting out the presentational components in our designs, we’ve dramatically decreased the amount of time our frontend engineers spend writing custom CSS and increased the chance that a design will be implemented “to spec.”
  • Sharing styles across repos: Maintaining our UI Library as an independent repository enables us to share styles across all of our frontend repositories. The library uses semantic versioning, so each repo can subscribe to updates at its own pace.

Both the library and the styleguide are continuous works in progress, and I’m excited to see how they will evolve as our team grows.

Appreciation

Patrick Lewis and Nelle McDade, my teammates on Button’s Design Team, created the first version of our Design Language System. I’d also like to thank my fellow frontend engineers, especially Daniel McGrath and Will Myers, for their thoughtful input and many contributions to our UI Library.

Like what you read? Give Grace Kwan a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.