Design Systems CLI: Multi-Build CSS

In January of this year, my team at Intuit open-sourced our project Design Systems CLI. It’s a tool that simplifies every part of developing components and libraries in a design system. Since then, we’ve been hard at work scaling and improving the project for teams inside and outside of Intuit. I’m excited to announce that we recently released some new features to simplify CSS imports and enable multiple CSS builds for design systems.

Wire frame designs for a design system
Photo by Halacious on Unsplash

Building and publishing CSS can be complicated, which is why many developers choose to use a CSS-in-JS solution like Styled Components. This post is not focused on the trade-offs of that architecture, but our team has found PostCSS to be more ideal for performance and customization in our design systems. This is why our initial release of DS-CLI was focused on making PostCSS-based builds simple to configure for any design system.

What We Had

// Example directory structure for built design system component.dist/
esm/
index.js
commonjs/
index.js
main.css

For someone using that component, they would then need to import JavaScript and CSS separately:

import { Example } from '@ds/example';
import '@ds/example/dist/main.css';

This is a fairly standard pattern that you’ll see in the documentation for projects like Webpack loaders and NextJS. While CSS and JavaScript are two separate things, in a design system component they can rarely function without the other also being loaded. This leads to some common problems:

  • People have to remember to import CSS. We would frequently have teams using a component for the first time without knowing they needed to import the CSS. This was even harder for teams who use a mix of CSS and CSS-in-JS which does not need imports.
  • Your CSS imports rely on the folder structure of your built output, which could change.
  • Nested CSS causes problems. Many of our components depend on other components — Icon is a common one. In order to load both we either needed to: include the Icon CSS in every other component’s CSS (creating duplicates), ask people to import dependent CSS (poor developer experience), or create tools to help.

This problem led us to build babel-plugin-include-styles, which automatically would search for design systems components and import dependent CSS. This worked in many cases, but required extra configuration and would break if you had multiple versions of a component. It was still far from ideal, and as a result some developers would have to import multiple CSS files to import a single component.

A New Set of Constraints

  • Automatic imports by default.
  • Compatibility with most popular project templates like CRA and Next.
  • Support for multi-build CSS for optimization.

Why multiple builds?

Progress

PostCSS Themed Changes

Design Systems CLI — CSS Imports

Design Systems CLI — Multi-Build CSS

Design Systems CLI — Switching Between CSS Builds

Design Systems CLI — NextJS Support for CSS imports

Overall, these changes allowed us to meet all of the goals we outlined for our CSS builds. CSS importing is easier for design systems users, and should work with little configuration in most projects. We can also produce multiple CSS builds to segment features, while allowing component consumers to switch between the different files easily.

Example

Let’s say you have two products, Foo and Bar, which each have a different color scheme. You can quickly scaffold a design system to output themed components in a variety of ways.

// Example directory structure for built design system component.components/
Button/
src/
index.ts
theme.ts
postcss/
main/
postcss.config.js
foo/
postcss.config.js
bar/
postcss.config.js

When built, this will produce a component with three CSS output files:

dist/
esm/
index.js // Automatically imports main.css
commonjs/
index.js
main.css // Includes Foo and Bar themes
foo.css // Includes only Foo themes
bar.css // Includes only Bar themes

Anyone using your components will get both themes included by default, and the component can switch between them. However, a team working on the Foo product can exclude the Bar theme using babel-plugin-replace-styles, which will instead load foo.css. Doing this saves them ~20% of the CSS cost and removes the need for a theme provider.

Hopefully this illustrates some of the possibilities using the new features! Components are easier to use, and there are lots of optimization opportunities you can make when you have multiple build artifacts.

Contact

Software Engineer in San Diego, CA

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store