Design Systems: Build versus Buy

In my previous article, I discussed getting started with a design system from an internal perspective. Now let's get into the fun part - actually building your design system! One of the questions folks in your organization may ask is:

Should we build our own design system from scratch? There are tons of existing, open sourced design systems out there - can we use one of those instead?

So when should you "build" versus "buy" when it comes to design systems? I'll share my thoughts.

Buy

There are a ton of really great existing component libraries out there that you can use as a base for building your design system. My favorite at the moment is chakra-ui. Chakra's landing page sells it really well "Chakra UI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications.". For designers, there's also a Figma Kit available.

By choosing the "buy" path, what we really mean here is using something from the open source community. Something like Chakra comes with a ton of existing components that you won't have to write yourself. Go browse their site for a bit and see what I mean! Ultimately, it could save you a ton of time and really jump start your progress on a design system.

What are things I personally look for in component libraries?

  • Do they allow theming so you can style the components to match your design language?
  • Do they have a focus on web accessibility?
  • How frequently do they do new releases?
  • How many open issues on GitHub do they have?
  • Are the contributors still active?
  • Do they accept outside contributions?

From a high level, building your design system from an existing component library typically looks like:

  • Choose an existing component library
  • Determine your design language (color palettes, typography, etc.)
  • Theme the component library to match your design language
  • Remove any unwanted functionality or styles (e.g., say they provide a loading state for a button, but you don't want that)
  • Start integrating it into your product(s)

Increased Velocity

Do you have a really small team? If so, choosing an existing component library to base your design system off of will save you a ton of time. Your velocity will be way faster by not reinventing the wheel. By instead using something off the shelf, you can spend more time theming it to your needs and shipping value to your customers.

Community Driven Updates

By using an existing component library that is open source, you get feature updates and bug fixes from the open source community. This has huge benefits, because it means you can spend less time fixing bugs with the underlying components. Sometimes all it takes is filing an issue on GitHub and a developer will resolve it. If you're a developer yourself, you can also contribute directly to the project by opening a pull request (please do this if you can!).

If you're an organization using an open source library for your design system, I highly recommend considering how your organization can support the contributors directly. I highly recommend buying their premium version (Chakra Pro, for example), sponsoring them via GitHub, sponsoring via open collective, or any other means they mention in their README or main site. Throwing a few hundred or thousand dollars their way is a drop in the bucket for most organizations, but could help the individual contributors a ton! After all, if your business is reliant on their work, the organization should want the project to be regularly maintained and successful.

Overrides

Overriding component styles and props you don't want to support can be tedious if your designs differ quite a bit from what it offers out of the box. An example could be your design system having highly customized CSS for a component. Also removing unwanted functionality can take a bit of time. For example, as mentioned above, say there is a button component that has a loading state and prop; however, your design system does not want to expose that. You'll need to remove the prop from the TypeScript interface as well as removing the underlying functionality. If someone is used to using the component library out of the box, they may be confused as to those props or styles aren't available.

Another downside with overrides is potentially shipping unused CSS. This is more of a problem with the very heavy-handed component libraries, but is worth mentioning. Some libraries provide existing, base styles which most folks end up overriding to fit their needs. The problem though, is the base styling is still shipped to the browser. The result is more CSS downloaded that provides no real value. This issue isn't in every component library, but is worth checking out if you'll be affected. This is arguably a nit-pick on my part, but something I consider when reviewing existing libraries.

Breaking Changes / Migrations

Most software projects introduce breaking changes. These are inevitable! It's great getting new features and bug fixes, but there comes a time when things need to change and require a breaking update. Most projects follow semantic-versioning, but some do not, which can lead to unexpected breaking changes in minor and patch releases. This ultimately means you may spend a lot of time on upgrades; however, it's all worthwhile in my opinion due to the tradeoff of the initial jump start.

Verdict of Buy

ProsCons
Higher initial velocityOverrides can be tough
Community driven updatesSome libraries don't follow semantic versioning
Figma kits may be available for designersLibrary UI/UX may influence design direction

Build

Building your design system from scratch can take a lot of time. If you're not under strict time constraints and want full control over the underlying output (HTML/CSS/JS), the "build" path is great. You'll have full control of the components and their APIs. By building these components from scratch, you'll also gain a better understanding of how semantic HTML works.

From a high level, building your design system from scratch typically looks like:

  • Determine your design language (color palettes, typography, etc.)
  • Build components in Figma
  • Build components in code
  • Start integrating it into your product(s)

Semantic HTML + Headless Solutions for a Hybrid Approach

For many of the components in a design system, you can use semantic markup. Take a Button component for example - the <button> HTML tag is great! You should use semantic HTML as much as possible - it's helps with accessibility! Potentially a spicy take (🌶️): but a lot of the building block components are not difficult to build using semantic HTML. Buttons, text, avatars, inputs, textareas, checkboxes, etc. are all backed by HTML tags that have been around for a really long time and are accessible to everyone.

const Button = ({ children, ...rootProps }) => {
  return (
    <button
      type="button"
      className="rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
      {...rootProps}
    >
      {children}
    </button>
  );
};

How about the more complex components though? The really difficult ones? Tooltips, custom selects, popovers, etc.? For these more complicated components, I highly recommend using existing packages that solve a lot of these problems. They're incredibly difficult to get right from an accessibility perspective and by reaching for a community driven solution, you'll save yourself a bunch of time and headaches. Some of my favorite libraries solving these problems are:

The future is near though! Browsers are starting to ship really helpful elements to assist here. Some of these I actually just ran across this weekend! A few examples:

  • dialog
    • Modals/Dialogs built-in to the browser! No longer a need to do an overlay and position everything yourself!
    • Supported in all major browsers
  • popover
    • That's right! Popovers straight from the browser rather than using popperjs/floating-ui!
    • Check out this great video from Una Kravets sharing some of the details
  • selectmenu
    • Wow! Even custom select components are coming soon™! So exciting!
    • Another timestamped video from Una Kravets showing the power of selectmenu

Verdict of Build

ProsCons
Full control over HTML/CSS/JS outputLower initial velocity
Gain a lot of experience building things from scratchMore maintenance unless you have a dedicated team
More design freedom / not feeling tied to an existing systemNo community driven updates (unless you're lucky!)

Final Thoughts

So, which one is right for you? It depends! Both have advantages and disadvantages. If I were on a small team that had a lot of experience building design systems, I'd probably do a hybrid approach as mentioned above. If I were on a less experienced team or the sole developer, I'd probably reach for building on top of an existing component library. From experience, it doesn't really matter which one you pick - it's just code! You can always pivot in one direction or the other. What's more important is the team you create to build the design system (and the friends you make along the way ❤️)!


Bonus Section: Design System Dream for Development

From a development perspective, here is an idea I've had for a while now:

  • Use TailwindCSS
  • Use something along the lines of @figma-export/cli to export design tokens such as colors and typography from Figma and into a TailwindCSS config. This means you'll have Figma and code in sync. You could also use this for exporting icons if you wanted.
  • Setup a job that will run this command for you automatically and do a diff to determine if there are new changes. If so, create a PR and automatically add reviewers for approval. (NOTE: Automating this sounds super dope)
  • Build all of the semantic elements from scratch (button, input, textarea, checkbox, toggle, radio groups, etc.)
  • Use something like headlessui for the more complex scenarios (popovers, selects, modals, etc.)
  • Build and bundle the package with vite/esbuild
  • Write lots of integration tests!
  • Add visual regression testing that's built into the PR review process
  • Open source on GitHub with changesets for helping with releases
  • Write a documentation site using Next.js + mdx + your design system to dog food it internally. Deploy on Vercel.

I'm hoping I can do this one day! I should write more as to why I think this is a good idea. Maybe an upcoming post! If you end up doing this, let me know how it goes!