Emmanuel Yusufu Aspiring Product Designer · Frontend & JS Developer · Certified Digital marketer

Building a design system with Grommet and Storybook

6 min read 1729

Building A Design System With Grommet

Introduction

We’ve all heard the buzzword “design system” thrown up and down. But how do we make sense of it, and more importantly, how do we build one for ourselves? This post will guide you through creating a design system using React, Grommet, and Storybook.

What exactly is a design system?

A design system consists of a design language, a component library, and a documentation/style guide website where the first two live.

As detailed in this talk by Emma Wedekind, when developers talk about design systems, they mostly refer to the component library; when designers do, they refer to the design language. Project managers are most likely concerned with the documentation/style guide website where everything lives.

The following expatiates on the purpose of these three pieces:

  • Design language: This helps shape our products into a representation of our brand. It includes color (for emotion), type scale, accessibility, and motion (for personality).
  • Component library: These are reusable code components that serve as the building blocks of our application. They also give expression to the design language. These include buttons, input fields, etc. If you aren’t building yours from scratch, you could customize a popular library such as Grommet to your project needs.
  • Documentation/style guide website: This is the site where you document everything about the design language and component library. This creates consistency by allowing all your team members to access it for use.

Design systems generally provide building blocks with which we construct our applications. It leads to consistent user experience across a company’s suite of products as they become more complex and its teams grow in size.

All major software companies have their own design system. Some examples of popular design systems are Shopify’s Polaris, Google’s Material Design, and Trello’s Nachos.

So, how do you build one for your own brand? You first need to have the three pieces of a design system in place.

Grommet

Grommet is a React component library that provides responsive and accessible mobile-first code components you can easily use for your projects.

Of particular note is the fact that Grommet provides powerful theming tools that let you tailor the component library to align with your color, type, component interaction, and layout needs. This addresses the design language piece we discussed earlier.

Storybook

Storybook is an open source tool that allows you to document and explore your code’s components. It works with React, Vue, and Angular, and it also enables you to work on your components in isolation and test them in different states.

Most importantly, Storybook enables automatic generation of a customizable style guide site.

Installation

To get started, let’s scaffold a blank React app.

npx create-react-app my-app
cd grommet-storybook-react-app

Open the project in your code editor. The next step is to install Grommet:

npm install grommet styled-components --save

Also install Storybook:

npx -p @storybook/cli sb init

Recall that Storybook can be used for React, Vue, and Angular. The above command will detect which framework we are using and install the appropriate Storybook version. In our case, the React version @storybook/react will be installed.

After the installation, run the app:

npm run storybook

The following screen will be displayed in the browser:

Storybook's Default Welcome Component

As you can see in the component explorer, Storybook provided two components out of the box for demo purposes. These are the Welcome and Button components.

If we look into our project folder, we’ll notice .storybook and stories folders have been added after the installation.

Stories Folder In Our Project Directory

Visit the stories/index.js folder to see the demo components. Observe and remove or comment out the portion below; we will be building our own welcome and button components.

storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);

storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
  .add('with some emoji', () => (
    <Button onClick={action('clicked')}>
      <span role="img" aria-label="so cool">
        😀 😎 👍 💯
      </span>
    </Button>
  ));

This is what should be left:

import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';

Default Storybook Components Removed

Creating our components

Now, onto the main thing: we’ll start by building welcome and button components just like we saw earlier.

One of the qualities of any good documentation is that it has a welcome note that helps developers get started. This usually provides a setup or installation guide. Let’s pretend we were building a design system for your company. We could use the welcome component as our welcome note. That means it will just be filled with plaintext.

The Welcome component

Create a components folder inside the src folder, then create a Welcome.js file inside components. Place the following code into the file:

import React from 'react';
import { Heading, Paragraph, Box } from 'grommet';

class Welcome extends React.Component {

  render() {
    return (
      <Box direction="column" pad="medium">

        <Heading margin="none" level="3">Chapter 1</Heading>
        <Paragraph margin="none">
          Lorem ipsum dolor sit amet,
          consectetur adipiscing elit,
          sed do eiusmod tempor incididunt ut
          labore et dolore magna aliqua.
        </Paragraph>
        <Paragraph margin="none">
          Lorem ipsum dolor sit amet,
          consectetur adipiscing elit,
          sed do eiusmod tempor incididunt ut
          labore et dolore magna aliqua.
        </Paragraph>

      </Box>
    );
  }

}

export default Welcome;

In the above code, we imported Heading and Paragraph, which are type components from Grommet. We also imported Box, which is a container to lay out our content. Everything is inside a Welcome component, which we have set up for export.

Inside the stories folder, go to index.js and import the Welcome component.

import Welcome from '../components/Welcome';

Also add the following to index.js:

storiesOf('Welcome', module).add('getting started', () => <Welcome />);

In Storybook, a story refers to a component and several possible states of it — for example, a button in different states.

In the code above, the storiesOf() function helps us name a particular story in the component explorer interface, and the .add() function sets up a dropdown, which, when released, presents different states of the component. This will be clearer when we create our next component.

Custom Welcome Component In Storybook

The Button component

Buttons are used a lot in interfaces. They have great impact on products and are a great place to start when creating a design system.

To create the Button component, create a Button.js file inside the components folder. Paste the following code in:

import React from 'react';
import { Button as GrommetBtn } from 'grommet';


class Button extends React.Component {
  render() {
    return (
      <GrommetBtn label="default" />
    );
  }
}

export default Button;

Notice that because I want to create our own component called Button, I had to rename Grommet’s default button to GrommetBtn to prevent name clashing.

Now import the Button component in index.js:

import Button from '../components/Button';

Add the button story to the same file:

storiesOf('Button', module).add('basic', () => <Button />);

This will render out as shown:

Custom Primary Button In Storybook

As expected in any design system, we should be able to reuse a component like our button in many situations. This is usually done by passing custom props to the component.

Whatever props are passed will affect the way a component is rendered. Fortunately, Grommet comes with enough props for each component to fill your needs.

To ensure our button component receives any props passed to it from index.js, go to Button.js and add an expression between curly braces.

<GrommetBtn label={this.props.label} {...this.props} />

Now let’s create and document different variations of our button. Go to the button story and make the following changes:

storiesOf('Button', module)
  .add('primary', () => <Button primary label="primary" />)
  .add('secondary', () => <Button primary={false} label="secondary" />)
  .add('plain', () => <Button plain label="plain" />)
  .add('Disabled', () => <Button disabled label="Disabled" />);

When we run the code, we should see the following screen:

Custom Secondary Button In Storybook

Grommet buttons have lots of useful props, such as accessibilityTitle, active, icon, plain, target, size, onClick, href, id, label, type, etc. Feel free to check them all out here.

It is worth mentioning that Storybook can be supercharged with add-ons, which you can see here. Add-ons are extra features that can be appended to Storybook to make it more useful and add advanced functionality.

Examples are the Source add-on (which lets you view the component’s source code to copy and paste), the notes add-on (for documenting component usage in markdown), and the background add-on for storybook customization.

Customizing the Grommet theme

We’ll now take a look at three ways you can customize your Grommet theme.

  1. Grommet components have a top-level wrapper container that can be used to style components globally.
const customTheme = {
    global: {
       colors: {
       custom: "#cc6633"
       }
    }
};

const Themed = () => (
  <Grommet theme={customTheme}>
    <Box pad="medium">
      <Anchor icon={<Add />} label="Add" color="custom" />
    </Box>
  </Grommet>
);
  1. Grommet also provides ThemeContext.Extend to style components locally.
<ThemeContext.Extend
  value={{ global: { colors: { doc: '#ff99cc' } } }}
>
  <Box pad="large" background="doc" direction="row" />
  <Box pad="large" background="doc" direction="row" />
</ThemeContext.Extend>
  1. You can also use styled-components to style components individually.
import styled from 'styled-components`;
import { Button } from 'grommet';
const MyStyledButton = styled(Button)`
   font-weight: bold;
   background-color: #BADA55;
`;

const MyComponent = () => (
  <div>
     Press Button <MyStyledButton label="Press Me" />
  </div>
);

Deploying your documentation as a static app

In order to showcase our component library to our whole team, we have to deploy it as a site. You can deploy it to Netlify, GitHub pages, or any static hosting service.

Simply add the following npm script to your package.json:

{
  "scripts": {
    "build-storybook": "build-storybook -c .storybook -o .out"
  }
}

Then run yarn build-storybook.

This will build Storybook from the project directory into a static web app and place it inside the .out directory. You can now deploy the content in the .out directory wherever you want.

To test it locally:

npx http-server .out

Conclusion

In this post, we’ve explored the technologies that will enable you to build a design system. We’ve covered how to implement components, customization, documentation, and hosting your design system. Now go build something awesome!

Plug: , a DVR for web apps

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Emmanuel Yusufu Aspiring Product Designer · Frontend & JS Developer · Certified Digital marketer

Leave a Reply