Start your app the right way! Featuring React, styled-system, styled components and Typescript
Tags: react, typescript, styledsystem, styledcomponents
Published: 2019-11-28 16:10:30

As an assignment for an app, I needed to implement a more consistent typography/theme. Consistency through your app is essential, and it also saves you much time. So instead of refactoring those buttons to set your font family, your margins, or your primary color, you can start with this and not worry about it anymore. There were numerous articles on how to approach this, and it's not something new, but writing about it seems like an excellent exercise to put what I've learned to practice. While reading these articles, I came across [styled-system] (https://github.com/styled-system/styled-system), an impressive library that makes it easy to make props available for your components. This library will be the foundation of this article. The articles will be listed at the end of the article.

I hope you enjoy this step by step tutorial on how to set up a style guide foundation for your app. We are going to do everything from scratch, starting with creating your app up until the style guide showcase in storybook. Let's get started!

If you don't fancy reading through the setup because you've done this a thousand times, here is how this article is written:

How this article is written:

- [Setup](#setup)
- [Theme structure](#theme-structure)
- [Dynamic component](#dynamic-component)
- [Setting up the typography styles and components](#typography)
- [Reference story in Storybook](#storybook-reference)
- [Wrapping up](#wrapping-up)
- [Sources](#sources)

## Setup <a name="setup"></a>

### Creating the app and installing extra packages

The create react app is a great start for a react app, we will use the [Typescript](https://www.typescriptlang.org/) flag to get all the nice features typescript gives us (especially those typescript 3.7 features)

`npx create-react-app react-styleguide --typescript`

Then we will install the other packages we need:

`yarn add styled-components @types/styled-components styled-system @types/styled-system react-router-dom`

Next we can add our dev dependencies: 

`yarn add -D prettier eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-config-prettier eslint-plugin-prettier`

The last thing we have to install is storybook. Storybook will be used to create a reference showcase of our theme ( typography, colors, space, breakpoints).

`npx -p @storybook/cli sb init --type react`

Storybook is a convenient tool you can use to prototype your components. I wrote an article about Storybook Driven Development a while ago that also introduces the basics of storybook. You can read it [here](https://dzone.com/articles/practical-guide-to-storybook-driven-development).

### Prettier & Eslint config

We want our code to be nice and neat, so we are going to use  [prettier](https://prettier.io/) and  [eslint](https://eslint.org/) to keep us in our lanes. These were already installed in the steps before, but now we have to configure them.

Our eslint (.eslintrc) setup will look something like this:

```javascript
{
      "parser": "@typescript-eslint/parser",
      "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:@typescript-eslint/recommended",
        "prettier/@typescript-eslint",
        "plugin:prettier/recommended"
      ],
      "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module",
        "ecmaFeatures": {
          "jsx": true
        }
      },
      "rules": {
        "prettier/prettier": [
          "error",
          {
            "singleQuote": true
          }
        ],
        "react/prop-types": "off",
        "@typescript-eslint/no-explicit-any": "error"
      },
      "settings": {
        "react": {
          "version": "detect"
        }
      }
    }
```
And prettier (.prettierrc) like this:

```javascript
{
  "trailingComma": "es5",
  "tabWidth": 2,
  "singleQuote": true,
  "printWidth": 140
}
```
Both these files need to be created in the root of your project. 

### Storybook setup

Luckily for us, Storybook now works out of the box with Typescript. We just have to setup up a few things for future use. We will setup our ThemeProvider and the Router ( for Links ). You can copy-paste the code below in your config.js in the .storybook folder.

```javascript
import { addDecorator, configure } from '@storybook/react';
import { ThemeProvider } from 'styled-components';
import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

addDecorator(story => (
  <ThemeProvider theme={{}}>
    <Router history={history}>{story()}</Router>
  </ThemeProvider>
));

configure(require.context('../src', true, /\.stories\.tsx$/), module);
```

We do a few things in this file:

- Setup the `ThemeProvider` with an empty object for now, because we don't have a theme yet
- Setup our `Router`, so our `Link` component doesn't break in storybook
- Change the `context` function, so it picks up our `.tsx` files.
- Change the folder where our `context` function searches for stories ( I like to keep my stories together with the components)

Next, we can change the extension of the demo story that storybook provides (0-Welcome.stories.js ) from `js` to `tsx` and move it in our `src` folder to see if everything is working. Try running `yarn storybook`, and if you see the screen below, you have your storybook setup done. Good job! 🔥

![Storybook running](https://thepracticaldev.s3.amazonaws.com/i/b8r3yai776d4pgd11cr8.png)

If you don't see this, don't worry, I got you. You can just clone this branch I made as a checkpoint [branch](https://github.com/jdcas89/react-styleguide/tree/step-1) so you can continue to follow the tutorial

![Alt text of image](https://media.giphy.com/media/26DMXQsL2vTC1Xgcw/giphy.gif)

Now we are ready to get our hands dirty, let's start with our theme structure! 

## Making our Theme structure <a name="theme-structure"></a>

Our theme is the skeleton of the app, this is where we define our standards ( most of them ) and how these should be used. We are heavily relying on styled-system for this. There are a few things we want to define in our theme:

- Space
- Breakpoints ( for responsive fonts )
- Colors

Let's start by making a folder in our src folder called styleguide. In this folder, we can create a `defaulTheme.ts`. In this file, we will define our theme. This theme will have the structure of the type provided by `styled-system`. You can read the specification of the theme [here]([https://styled-system.com/theme-specification/](https://styled-system.com/theme-specification/))

We won't be using all these properties for this article, as it would just be too much work and probably a bit boring to read. So we are going to keep it simple by only setting up space, breakpoints, and colors.

#### Space
The point of defining your space is to avoid having inconsistent pixels all over the place in your app. With predefined space values, you are going to have more predictable results. You can set this up however you want, but I like the geometric progression explained in this [article] (https://medium.com/eightshapes-llc/space-in-design-systems-188bcbae0d62) combined with the t-shirt sizes approach. It looks something like this:

```typescript
export interface Space {
  NONE: number;
  XS: number;
  S: number;
  M: number;
  L: number;
  XL: number;
  XXL: number;
}

export const space: Space = {
  NONE: 0,
  XS: 2,
  S: 4,
  M: 8,
  L: 16,
  XL: 32,
  XXL: 64,
};

```
#### Breakpoints

Next are our breakpoints. These breakpoints are mostly used with responsive fonts, I'll show it works later.


```typescript
export const breakpoints: string[] = ['319px', '424px', '767px', '1023px'];
```
#### Colors
These colors are highly opinionated, and you can set up your colors the way you want. I'll just give you an idea of how to set them up. Notice that I am using the csstype that styled-system uses.
 
```typescript
import * as CSS from 'csstype';

export interface ThemeColors {
  primary: CSS.ColorProperty;
  link: CSS.ColorProperty;
  success: CSS.ColorProperty;
  warning: CSS.ColorProperty;
  error: CSS.ColorProperty;
  heading: CSS.ColorProperty;
  text: CSS.ColorProperty;
  disabled: CSS.ColorProperty;
  border: CSS.ColorProperty;
}

export const colors: ThemeColors = {
  primary: '#423EA2',
  link: '#1890ff',
  success: '#52c41a',
  warning: '#faad14',
  error: '#e84118',
  heading: '#423EA2',
  text: '#000',
  disabled: '#f5222d',
  border: '#423EA2',
};

```
At last, our defaultTheme.ts:
```typescript
import { Theme } from 'styled-system';
import { colors } from './colors';
import { space } from './space';

export const breakpoints: string[] = ['319px', '424px', '767px', '1023px'];

export const defaultTheme: Theme = {
  space: {
    ...space,
  },
  breakpoints,
  colors: {
    ...colors,
  },
};

```

Once you define these you can use them in components like this: 

```typescript

const Button = styled.button`
  background-color: ${({ theme }) => theme.colors.primary};
  padding: ${({ theme }) => theme.space.M}px;
  margin: ${({ theme }) => theme.space.M}px;
  width: 200px;
  border: none;
  color: white;
  font-size: 14px;
  font-weight: 700;
  border-radius: 15px;
  letter-spacing: 2px;
  text-transform: uppercase;
`;
```

And the result :

![Button](https://thepracticaldev.s3.amazonaws.com/i/vpmyecta8cerikf90h6m.png)

## Creating our dynamic styled component<a name="dynamic-component"></a>

Next up, we will be making our dynamic styled component with all the styled-system props built-in. Here is were styled-system shines as we only have to use the style functions they provide.

```typescript
import React from 'react';
import styled from 'styled-components';
import {
  borderRadius,
  BorderRadiusProps,
  color,
  fontFamily,
  FontFamilyProps,
  fontSize,
  FontSizeProps,
  fontStyle,
  FontStyleProps,
  fontWeight,
  FontWeightProps,
  letterSpacing,
  LetterSpacingProps,
  lineHeight,
  LineHeightProps,
  size,
  SizeProps,
  space,
  SpaceProps,
  textAlign,
  TextAlignProps,
  textStyle,
  TextStyleProps,
} from 'styled-system';

export type StyledSystemProps =
  | SpaceProps
  | FontSizeProps
  | FontStyleProps
  | SizeProps
  | TextStyleProps
  | LetterSpacingProps
  | FontFamilyProps
  | FontWeightProps
  | BorderRadiusProps
  | FontFamilyProps
  | LineHeightProps
  | TextAlignProps
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  | { color: string; as?: keyof JSX.IntrinsicElements | React.ComponentType<any> };

export default styled.div`
  ${space}
  ${fontSize}
  ${fontStyle}
  ${size}
  ${color}
  ${textStyle}
  ${letterSpacing}
  ${fontFamily}
  ${fontWeight}
  ${borderRadius}
  ${lineHeight}
  ${textAlign}
`;
```

This means that this component now has a bunch of props ready to be used for easy styling. This dynamic component will be the foundation for setting up all our typography types.

## Setting up all the typography styles and components<a name="typography"></a>

Now we can start creating our text styles. I am not an expert on typography, but these guidelines from [BBC] (https://www.bbc.co.uk/gel/guidelines/typography) will give you a good introduction to typography harmony.  Our text styles will look like this:

```typescript
import { colors } from './colors';
import { StyledSystemProps } from './DynamicStyledSystemComponent';

const fontFamilies: { heading: string; body: string } = {
  heading: 'Montserrat, serif',
  body: 'Raleway, sans-serif',
};

interface TypographyStyles {
  H1: StyledSystemProps;
  H2: StyledSystemProps;
  H3: StyledSystemProps;
  H4: StyledSystemProps;
  H5: StyledSystemProps;
  LargeLead: StyledSystemProps;
  SmallLead: StyledSystemProps;
  Paragraph: StyledSystemProps;
  SmallParagraph: StyledSystemProps;
  Link: StyledSystemProps;
}
export const typographyStyles: TypographyStyles = {
  H1: {
    fontSize: [50, 51, 52, 57],
    fontWeight: 700,
    fontFamily: fontFamilies.heading,
    as: 'h1',
  },
  H2: {
    fontSize: [37, 39, 41, 43],
    fontWeight: 700,
    color: colors.primary,
    fontFamily: fontFamilies.heading,
    as: 'h2',
  },
  H3: {
    fontSize: [27, 28, 30, 32],
    fontWeight: 700,
    fontFamily: fontFamilies.heading,
    as: 'h3',
  },
  H4: {
    fontSize: [18, 20, 22, 24],
    fontWeight: 700,
    color: colors.primary,
    fontFamily: fontFamilies.heading,
    as: 'h4',
  },
  H5: {
    fontWeight: 700,
    fontSize: [16, 17, 19, 21],
    fontFamily: fontFamilies.heading,
    as: 'h5',
  },
  LargeLead: {
    fontWeight: 500,
    fontSize: [18, 20, 22, 24],
    fontFamily: fontFamilies.heading,
    as: 'p',
  },
  SmallLead: {
    fontWeight: 500,
    fontSize: [17, 18, 19, 21],
    fontFamily: fontFamilies.heading,
    as: 'p',
  },
  Paragraph: {
    fontSize: [14, 15, 15, 16],
    fontWeight: 300,
    fontFamily: fontFamilies.body,
    as: 'p',
  },
  SmallParagraph: {
    fontSize: [13, 14, 14, 15],
    fontWeight: 300,
    fontFamily: fontFamilies.body,
    as: 'p',
  },
  Link: {
    fontWeight: 700,
    color: colors.primary,
    fontSize: [14, 15, 15, 16],
    fontFamily: fontFamilies.body,
  },
};
```

With these text styles, we can create typography components. We can create a helper function createComponent that has two arguments: the corresponding styling props, and it's display name. The Link component is not created with the createComponent function because it needs to use the react-dom Link component.


```typescript
import React from 'react';
import { Link as RouterLink, LinkProps } from 'react-router-dom';
import DynamicStyledSystemComponent, { StyledSystemProps } from './DynamicStyledSystemComponent';
import { typographyStyles } from './typographyStyles';

type AnchorProps = StyledSystemProps & Pick<LinkProps, 'to'> & { onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void };

const Link: React.FC<AnchorProps> = ({ to, onClick, children, ...props }) => (
  <RouterLink to={to} onClick={onClick}>
    <DynamicStyledSystemComponent {...typographyStyles.Link} {...props}>
      {children}
    </DynamicStyledSystemComponent>
  </RouterLink>
);

interface TypographyComponentProps {
  H1: React.FC<StyledSystemProps>;
  H2: React.FC<StyledSystemProps>;
  H3: React.FC<StyledSystemProps>;
  H4: React.FC<StyledSystemProps>;
  H5: React.FC<StyledSystemProps>;
  LargeLead: React.FC<StyledSystemProps>;
  SmallLead: React.FC<StyledSystemProps>;
  Paragraph: React.FC<StyledSystemProps>;
  SmallParagraph: React.FC<StyledSystemProps>;
  Link: React.FC<AnchorProps>;
}

const createComponent: (textStyle: StyledSystemProps, displayName: string) => React.FC<StyledSystemProps> = (textStyle, displayName) => {
  const component: React.FC<StyledSystemProps> = props => (
    <DynamicStyledSystemComponent {...textStyle} {...props}>
      {props.children}
    </DynamicStyledSystemComponent>
  );
  component.displayName = displayName;
  return component;
};

export const Typography: TypographyComponentProps = {
  H1: createComponent(typographyStyles.H1, 'H1'),
  H2: createComponent(typographyStyles.H2, 'H2'),
  H3: createComponent(typographyStyles.H3, 'H3'),
  H4: createComponent(typographyStyles.H4, 'H4'),
  H5: createComponent(typographyStyles.H5, 'H5'),
  LargeLead: createComponent(typographyStyles.LargeLead, 'LargeLead'),
  SmallLead: createComponent(typographyStyles.SmallLead, 'SmallLead'),
  Paragraph: createComponent(typographyStyles.Paragraph, 'Paragraph'),
  SmallParagraph: createComponent(typographyStyles.SmallParagraph, 'SmallParagraph'),
  Link: Link,
};
```

Now we can start using our Typography components. I will showcase this next in storybook.

## Reference story in Storybook <a name="storybook-reference"></a>

We can showcase how our styleguide is setup by making a storybook story. This will serve as a reference if you ever want to know which Typography, color, space you want to use.

```typescript
import React from 'react';
import { storiesOf } from '@storybook/react';
import { Typography } from './Typography';
import styled from 'styled-components';
import { colors } from './colors';
import { breakpoints } from './theme';
import { space } from './space';

const { H1, H2, H3, H4, H5, LargeLead, Link, Paragraph, SmallLead, SmallParagraph } = Typography;

storiesOf('Styleguide ', module)
  .addParameters({ viewport: { defaultViewport: 'default' } })
  .add('default', () => (
    <Container>
      <H2>Typography</H2>
      <Divider />
      <H1>H1: Animi aperiam, aspernatur culpa deserunt eaque, eius explicabo inventore ipsa laudantium</H1>
      <H2>H2: Consectetur consequuntur cum deserunt dignissimos esse fugiat inventore iusto, laboriosam maiores minima!.</H2>
      <H3>H3: Culpa dignissimos expedita facilis, fugiat minus odio reiciendis ut? Accusamus delectus dicta eius.</H3>
      <H4>H4: Accusamus ad adipisci alias aliquam aperiam autem, culpa dolorem enim error est eum.</H4>
      <H5>H5: Debitis distinctio dolorum fugiat impedit itaque necessitatibus, quo sunt? Atque consectetur, corporis.</H5>
      <LargeLead>LargeLead:Deleniti est facere id placeat provident sapiente totam vitae. Asperiores consequuntur eaque eum.</LargeLead>
      <SmallLead>SmallLead: At aut corporis culpa doloribus ea enim error est impedit, ipsum iure maxime molestiae omnis optio.</SmallLead>
      <Paragraph>
        Paragraph: Facilis hic iste perspiciatis qui quibusdam sint velit vero Animi doloremque esse ex iure perferendis.
      </Paragraph>
      <SmallParagraph>SmallParagraph: Ad animi at debitis eligendi explicabo facere illum inventore, ipsum minus obcaecati.</SmallParagraph>
      <Link to="/">Link: Lorem ipsum dolor sit amet, consectetur adipisicing elit.</Link>
      <Divider />
      <H2>Colors</H2>
      <Paragraph>These colors are defined in styleguide colors.ts.</Paragraph>
      <Divider />
      <GridContainer>
        <div>
          <SmallParagraph>Kind</SmallParagraph>
        </div>
        <div>
          <SmallParagraph>HEX</SmallParagraph>
        </div>
        <div>
          <SmallParagraph>Color</SmallParagraph>
        </div>
      </GridContainer>
      {Object.entries(colors).map(obj => (
        <GridContainer key={obj[0]}>
          <SmallParagraph>{obj[0]}</SmallParagraph>
          <SmallParagraph>{obj[1]}</SmallParagraph>
          <ColorCircle color={obj[1]} />
        </GridContainer>
      ))}
      <Divider />
      <H2>Breakpoints</H2>
      <Paragraph>These are the responsive breakpoints being used</Paragraph>
      <br />
      <FlexContainer>
        {breakpoints.map((key: string) => (
          <SmallParagraph key={key} m={4}>
            {key}
          </SmallParagraph>
        ))}
      </FlexContainer>
      <Divider />
      <H2>Space</H2>
      <FlexContainer>
        {Object.entries(space).map(obj => (
          <div key={obj[0]}>
            <SmallParagraph m={2}>
              <strong>{obj[1]}px</strong>
            </SmallParagraph>
            <SmallParagraph m={2}>{obj[0]}</SmallParagraph>
          </div>
        ))}
      </FlexContainer>
    </Container>
  ));

const Divider = styled.div`
  border: 1px solid #00000022;
  width: 100%;
  margin: ${({ theme }) => theme.space.M}px;
`;

const ColorCircle = styled.div<{ color: string }>`
  height: 20px;
  width: 20px;
  border-radius: 20px;
  background-color: ${({ color }) => color};
`;

const GridContainer = styled.div`
  display: grid;
  grid-template-columns: 150px 150px 150px;
  margin: ${({ theme }) => theme.space.M}px;
`;

const FlexContainer = styled.div`
  display: flex;
`;

const Container = styled.div`
  background-color: white;
  height: 100vh;
  padding: 16px;
`;
```

## Wrapping up <a name="wrapping-up"></a>

So this was my take on how to setup a styleguide for you next react app. Hope you enjoyed the post and until the next one! Cheers

[Github repo](https://github.com/jdcas89/react-styleguide)

## Sources

[https://levelup.gitconnected.com/building-a-react-typography-system-f9d1c8e16d55](https://levelup.gitconnected.com/building-a-react-typography-system-f9d1c8e16d55)
[https://www.bbc.co.uk/gel/guidelines/typography](https://www.bbc.co.uk/gel/guidelines/typography)
[https://sproutsocial.com/seeds/visual/typography/](https://sproutsocial.com/seeds/visual/typography/)
[https://medium.com/eightshapes-llc/space-in-design-systems-188bcbae0d62](https://medium.com/eightshapes-llc/space-in-design-systems-188bcbae0d62)