Skip to content

Instantly share code, notes, and snippets.

@chadlavi
Last active August 10, 2019 15:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadlavi/79a0ed9c169883038610941f85df827f to your computer and use it in GitHub Desktop.
Save chadlavi/79a0ed9c169883038610941f85df827f to your computer and use it in GitHub Desktop.
Creating a component library

Advice on setting up a component library

Contents

Get buy-in from product owners

Building a component library in an already-existing app ecosystem means a lot of work for everyone up front, but saves a lot more work in the long run.

You'll need your product owners to understand that, and be ok with it. Their developers and designers are going to need to do a lot of work that feels like re-tracing their steps in this process!

Find your components

Do an audit — with designers if possible, to get their buy-in and help.

  1. Where are you doing the same thing twice or more in slightly different ways? (buttons? inputs? Icons? Text styles?)
    • Figure out a uniform version to use (or a uniform base, with some clear variants), then base the component on that
  2. Do you already have any components that are being reused on a small scale?
    • Add that to the component library, maybe there are other places to reuse it, too
  3. Are there any places in the design that someone really hates/thinks is bad UX?
    • Take note of that. Building a component library is a great opportunity to address that sort of issue. If that bad user experience makes it into the library, it’ll get spread around; but if you make that into a good experience, that good experience will get spread around instead.

Find your styles

Chances are you not only have several different buttons, but also several different button styles. And more to the point, different button colors. And text colors. And background colors. And shadows! And borders!

Go find them. Put them all together in one place. This is your theme.

Build your library

I very strongly recommend creating a new repo for the library, and setting it up like this:

  • I’m assuming typescript and yarn
  • DO NOT put a web app in this repo; keep this a pure library. That means most of your dependencies are peer dependencies (react, for example), and you have very few dev dependencies (because you don’t have an app to run).
  • use yarn link to test out the library in one of your real apps. If you're not familiar with yarn link yet, it's an extremely useful tool!

Build your theme

Inside your library, I recommend splitting up your theme into a couple key categories:

  • colors

    This is what it sounds like. Color definitions. Ideally, you should have named colors, with indexed shade variants (I use variants 50, 100, 200, 300, 400, 500, 600, 700, 800, 900; 500 is the default shade for the color family, 50 is extremely light, 900 is very dark). For example: theme.colors.blue[500] or if you need more complex handling, something like theme.colors.blue[500].hex or theme.colors.blue[500].rgb, or even theme.colors.blue[500].compliment

  • typography

    Everything related to text styles. You should have named typography variants that cover all your text styling. There's likely quite a lot of consolidation that can be done here.

  • metrics

    Define your base unit and all your derivative units for measurments. This is a really powerful thing if you do it right. We use an 8px base, and set everything as a multiple of that -- for example, our base font size is 2 units (16px), our base padding for buttons is 2 units on the top and bottom, three on the left and right.

  • borders

    Define all the stuff about the edges of things. You should include focus state styling here, as well as shadows and different border styles (for things like inputs, buttons, modals, etc). This is also where you should define border radii.

  • spacing

    All the padding and margin stuff. It's best to make some canned spacing variants that can be used for multiple things later -- and be sure to use your base unit, not raw numbers. That way you can make wide reaching changes in your theme just by editing those base units later.

If you use a styling mechanism like emotion that operates on the JSS pattern, you can basically just store huge objects for all this stuff. All your colors go in theme.colors. Inside colors you have different categories -- this organization is fairly simple and is, as they say, left to the user. But suffice it to say you might end up with things like this in your library:

// let's say this is Button.style.ts

export default (theme: YourThemeType) => {

  const buttonBaseStyle = {
    ...theme.borders.borderRadius.default, // which is, let's say, `{ borderRadius: theme.metrics.unit / 2 }`, or 4px
    ...theme.borders.shadow.default,
    ...theme.borders.focus.default,
    ...theme.typography.button,
    color: theme.colors.text.default,
    backgroundColor: theme.colors.grey[500],
    // other default JSS stuff
  }

  return ({
    default: {
      ...buttonBaseStyle,
    },
    disabled: {
      ...buttonBaseStyle,
      backgroundColor: theme.colors.grey[200],
      color: theme.colors.grey[700],
      cursor: 'not-allowed',
    },
    error: {
      ...buttonBaseStyle,
      backgroundColor: theme.colors.red[500],
      color: theme.colors.white,
      ...theme.borders.focus.error,
    },
    primary: {
      ...buttonBaseStyle,
      backgroundColor: theme.colors.primary[500],
      color: theme.colors.white,
    },
    // other variants
  })
}

Publish your library

I think it’s possible to use a git repo as a dependency in other projects, but the solution we have at my work is to publish the library to our own private node package repository. We use jfrog’s “Artifactory” product. It’s... fine. The web interface is sort of bad, but you don’t really need to interact with it that much.

The way this works for us is just some custom stuff in our .npmrc file. I set up a simple setup script that the developer who wants to publish to/install from our private repo needs to run once, and it asks them for their Artifactory username and API key, then handles the rest. It looks like this:

#!/bin/bash

# your Artifactory repo URL goes here. It should look like `https://[team].jfrog.io/[repo]`
ARTIFACTORY_BASE_URL=

fatal() {
  echo "FATAL: $1" >&2
  exit 1
}

touch ./.env
source ./.env

if [ -z "$ARTIFACTORY_USER" ]; then
  echo -n "Enter Artifactory user "
  read ARTIFACTORY_USER
  echo -e "\nARTIFACTORY_USER=${ARTIFACTORY_USER}" >> ./.env
fi

if [ -z "$ARTIFACTORY_API_KEY" ]; then
  echo -n "Enter Artifactory API key: "
  read ARTIFACTORY_API_KEY
  echo -e "\nARTIFACTORY_API_KEY=${ARTIFACTORY_API_KEY}" >> ./.env
fi

echo "package-lock=false" > ./.npmrc \
&& echo "registry=${ARTIFACTORY_BASE_URL}/api/npm/npm/" >> ./.npmrc \
&& echo "save-exact=true" >> ./.npmrc \
&& curl --user "${ARTIFACTORY_USER}":"${ARTIFACTORY_API_KEY}" "${ARTIFACTORY_BASE_URL}/api/npm/auth" >> ./.npmrc || fatal 'coult not get artifactory credentials'

This also creates a .env file that stores the Artifactory username and API key, or just adds them to your .env if it already exists.

Another interesting thing about Artifactory's virtual node package repositories: it can act as a cache for all your node packages. We actually have all our projects set up so that they hit Artifactory first when someone does a yarn install, then if it's not in Artifactory yet, we install it from the original repo and save a copy in Artifactory for later.

Refactor your app(s)

This is the really painful part. All those places you identified where you were doing things in a one-off way? Time to redo them, using the component library. This will require buy-in from product owners, and might even be something that other developers do. This is a whole-dev-team activity, and it might take a while.

Refactoring is maybe boring, maybe unglamorous, and definitely tech debt! But it will (a) help find weaknesses in your components that can be strengthened and (b) make future development and changes way, way easier.

Document your components

Use something simple like Storybook to document your components. This should be something your designers can use and reference.

If you really want to go all out, you can build a docs site using your components, but this requires a mature component library, and also requires that your library include the kind of stuff that can be used to build a docs site.

Typescript means a lot of self-documentation for developers, but I highly recommend also finding a way to publish actual descriptions of your props in your documentation (so non-technical folk can see it, too).

Create design resources

You need some sort of design equivalent of your components, so your designers will be on the same basic set of rails as you — and so they’ll know when they’re designing something that’s a composition of existing components vs something that requires new components.

I haven’t done it because I work in Sketch and directly in the code, but if you have non-technical designers, I wonder how it feels to use that AirBnB thing that can generate sketch files from react code.

If not, then maybe just work with a designer to have them manually make a sketch component library.

Maintain it!

It's not a one-and-done thing, creating a component library -- you'll need to maintain it pretty actively to keep it current and complete. Ideally, someone needs to own this; maybe even as a full time job. We treat our component libraries (React for web, React Native for everything else) as a product, so me and my developers are considered alongside the other product teams. This seems like a good approach to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment