Theme setup for styled components & styled system
Tags: react, typescript
Published: 2021-09-01 13:49:25

### Introduction

Let us continue building our chakra components clone using `styled-components` & `styled-system`. In this tutorial we will be setting up the theme for our component library. For this we will first install `styled-components` and setup the `theme object` and create a `Provider component`. I would like you to first check the styled-system [api page](https://styled-system.com/api), it lists all the utility props provided by the library along with the **theme key** that a particular prop uses.

For an introduction to using `styled-components`, `styled-system`, and `typescript` together check my [introductory post](https://dev.to/yaldram/working-with-styled-system-using-typescript-4mkj). All the code for this tutorial can be found under the **theme-setup** branch [here](https://github.com/yaldram/chakra-ui-clone).

### Prerequisite

Please check the previous post where we have setup the component library. In this tutorial we will -

- Create the theme object.
- Create a Provider component where we pass the theme to styled-component's `ThemeProvider`.
- Build our library and test the provider in the `example/src/App.tsx`.

### Setup

- First let us create a branch, from the main branch run -

```bash
git checkout -b theme-setup
```

- Now let us install styled-components, note that we will install it as a devDependency and add its entry to peerDependencies under package.json. This means that the project that will use our chakra-ui-clone library will also need to install styled-components.

```bash
npm install --save-dev styled-components
```

- Under package.json -

```json
"peerDependencies": {
    "react": "^17.0.2",
    "styled-components": "^5.3.0"
  }
```

- Also install types for it -

```bash
npm install --save-dev @types/styled-components
```

- Then install styled-system as a dependency -

```bash
npm install styled-system
```

- Also install the types for it -

```bash
npm install --save-dev @types/styled-system
```

### Theme folder structure

- Create a theme folder under the src folder.
- Under the theme folder create 4 different files namely - `index.ts`, `colors.ts`, `spacing.ts` and `typography.ts`.

- So for the colors I copied them from the [chakra github code](https://github.com/chakra-ui/chakra-ui/blob/main/packages/theme/src/foundations/colors.ts).

- As oppose to chakra we will not use numbers as our object keys. Open colors.ts and paste the following -

```ts
export const colors = {
  transparent: "transparent",
  current: "currentColor",
  black: "#000000",
  white: "#FFFFFF",

  whiteAlpha50: "rgba(255, 255, 255, 0.04)",
  whiteAlpha100: "rgba(255, 255, 255, 0.06)",
  whiteAlpha200: "rgba(255, 255, 255, 0.08)",
  whiteAlpha300: "rgba(255, 255, 255, 0.16)",
  whiteAlpha400: "rgba(255, 255, 255, 0.24)",
  whiteAlpha500: "rgba(255, 255, 255, 0.36)",
  whiteAlpha600: "rgba(255, 255, 255, 0.48)",
  whiteAlpha700: "rgba(255, 255, 255, 0.64)",
  whiteAlpha800: "rgba(255, 255, 255, 0.80)",
  whiteAlpha900: "rgba(255, 255, 255, 0.92)",

  blackAlpha50: "rgba(0, 0, 0, 0.04)",
  blackAlpha100: "rgba(0, 0, 0, 0.06)",
  blackAlpha200: "rgba(0, 0, 0, 0.08)",
  blackAlpha300: "rgba(0, 0, 0, 0.16)",
  blackAlpha400: "rgba(0, 0, 0, 0.24)",
  blackAlpha500: "rgba(0, 0, 0, 0.36)",
  blackAlpha600: "rgba(0, 0, 0, 0.48)",
  blackAlpha700: "rgba(0, 0, 0, 0.64)",
  blackAlpha800: "rgba(0, 0, 0, 0.80)",
  blackAlpha900: "rgba(0, 0, 0, 0.92)",

  gray50: "#F7FAFC",
  gray100: "#EDF2F7",
  gray200: "#E2E8F0",
  gray300: "#CBD5E0",
  gray400: "#A0AEC0",
  gray500: "#718096",
  gray600: "#4A5568",
  gray700: "#2D3748",
  gray800: "#1A202C",
  gray900: "#171923",

  red50: "#FFF5F5",
  red100: "#FED7D7",
  red200: "#FEB2B2",
  red300: "#FC8181",
  red400: "#F56565",
  red500: "#E53E3E",
  red600: "#C53030",
  red700: "#9B2C2C",
  red800: "#822727",
  red900: "#63171B",

  orange50: "#FFFAF0",
  orange100: "#FEEBC8",
  orange200: "#FBD38D",
  orange300: "#F6AD55",
  orange400: "#ED8936",
  orange500: "#DD6B20",
  orange600: "#C05621",
  orange700: "#9C4221",
  orange800: "#7B341E",
  orange900: "#652B19",

  yellow50: "#FFFFF0",
  yellow100: "#FEFCBF",
  yellow200: "#FAF089",
  yellow300: "#F6E05E",
  yellow400: "#ECC94B",
  yellow500: "#D69E2E",
  yellow600: "#B7791F",
  yellow700: "#975A16",
  yellow800: "#744210",
  yellow900: "#5F370E",

  green50: "#F0FFF4",
  green100: "#C6F6D5",
  green200: "#9AE6B4",
  green300: "#68D391",
  green400: "#48BB78",
  green500: "#38A169",
  green600: "#2F855A",
  green700: "#276749",
  green800: "#22543D",
  green900: "#1C4532",

  teal50: "#E6FFFA",
  teal100: "#B2F5EA",
  teal200: "#81E6D9",
  teal300: "#4FD1C5",
  teal400: "#38B2AC",
  teal500: "#319795",
  teal600: "#2C7A7B",
  teal700: "#285E61",
  teal800: "#234E52",
  teal900: "#1D4044",

  blue50: "#ebf8ff",
  blue100: "#bee3f8",
  blue200: "#90cdf4",
  blue300: "#63b3ed",
  blue400: "#4299e1",
  blue500: "#3182ce",
  blue600: "#2b6cb0",
  blue700: "#2c5282",
  blue800: "#2a4365",
  blue900: "#1A365D",

  cyan50: "#EDFDFD",
  cyan100: "#C4F1F9",
  cyan200: "#9DECF9",
  cyan300: "#76E4F7",
  cyan400: "#0BC5EA",
  cyan500: "#00B5D8",
  cyan600: "#00A3C4",
  cyan700: "#0987A0",
  cyan800: "#086F83",
  cyan900: "#065666",

  purple50: "#FAF5FF",
  purple100: "#E9D8FD",
  purple200: "#D6BCFA",
  purple300: "#B794F4",
  purple400: "#9F7AEA",
  purple500: "#805AD5",
  purple600: "#6B46C1",
  purple700: "#553C9A",
  purple800: "#44337A",
  purple900: "#322659",

  pink50: "#FFF5F7",
  pink100: "#FED7E2",
  pink200: "#FBB6CE",
  pink300: "#F687B3",
  pink400: "#ED64A6",
  pink500: "#D53F8C",
  pink600: "#B83280",
  pink700: "#97266D",
  pink800: "#702459",
  pink900: "#521B41",

  linkedin50: "#E8F4F9",
  linkedin100: "#CFEDFB",
  linkedin200: "#9BDAF3",
  linkedin300: "#68C7EC",
  linkedin400: "#34B3E4",
  linkedin500: "#00A0DC",
  linkedin600: "#008CC9",
  linkedin700: "#0077B5",
  linkedin800: "#005E93",
  linkedin900: "#004471",

  facebook50: "#E8F4F9",
  facebook100: "#D9DEE9",
  facebook200: "#B7C2DA",
  facebook300: "#6482C0",
  facebook400: "#4267B2",
  facebook500: "#385898",
  facebook600: "#314E89",
  facebook700: "#29487D",
  facebook800: "#223B67",
  facebook900: "#1E355B",

  messenger50: "#D0E6FF",
  messenger100: "#B9DAFF",
  messenger200: "#A2CDFF",
  messenger300: "#7AB8FF",
  messenger400: "#2E90FF",
  messenger500: "#0078FF",
  messenger600: "#0063D1",
  messenger700: "#0052AC",
  messenger800: "#003C7E",
  messenger900: "#002C5C",

  whatsapp50: "#dffeec",
  whatsapp100: "#b9f5d0",
  whatsapp200: "#90edb3",
  whatsapp300: "#65e495",
  whatsapp400: "#3cdd78",
  whatsapp500: "#22c35e",
  whatsapp600: "#179848",
  whatsapp700: "#0c6c33",
  whatsapp800: "#01421c",
  whatsapp900: "#001803",

  twitter50: "#E5F4FD",
  twitter100: "#C8E9FB",
  twitter200: "#A8DCFA",
  twitter300: "#83CDF7",
  twitter400: "#57BBF5",
  twitter500: "#1DA1F2",
  twitter600: "#1A94DA",
  twitter700: "#1681BF",
  twitter800: "#136B9E",
  twitter900: "#0D4D71",

  telegram50: "#E3F2F9",
  telegram100: "#C5E4F3",
  telegram200: "#A2D4EC",
  telegram300: "#7AC1E4",
  telegram400: "#47A9DA",
  telegram500: "#0088CC",
  telegram600: "#007AB8",
  telegram700: "#006BA1",
  telegram800: "#005885",
  telegram900: "#003F5E",
};

export type Colors = typeof colors;

export type ColorScheme =
  | "whiteAlpha"
  | "blackAlpha"
  | "gray"
  | "red"
  | "orange"
  | "yellow"
  | "green"
  | "teal"
  | "blue"
  | "cyan"
  | "purple"
  | "pink"
  | "linkedin"
  | "facebook"
  | "messenger"
  | "whatsapp"
  | "twitter"
  | "telegram";

export const colorSchemeOptions = [
  "whiteAlpha",
  "blackAlpha",
  "gray",
  "red",
  "orange",
  "yellow",
  "green",
  "teal",
  "blue",
  "cyan",
  "purple",
  "pink",
  "linkedin",
  "facebook",
  "messenger",
  "whatsapp",
  "twitter",
  "telegram",
];
```

- Similarly under the spacing.ts file paste -

```ts
export const spacing = {
  xxs: "0.6rem",
  xs: "0.8rem",
  sm: "1rem",
  md: "1.2rem",
  lg: "1.5rem",
  xl: "2rem",
  xxl: "2.4rem",
  "3xl": "3rem",
  "4xl": "3.6rem",
};

export type Space = typeof spacing;
```

- And for typography.ts -

```ts
export const typography = {
  letterSpacings: {
    tighter: "-0.05em",
    tight: "-0.025em",
    normal: "0",
    wide: "0.025em",
    wider: "0.05em",
    widest: "0.1em",
  },

  lineHeights: {
    normal: "normal",
    none: 1,
    shorter: 1.25,
    short: 1.375,
    base: 1.5,
    tall: 1.625,
    taller: 2,
    xxs: ".75rem",
    xs: "1rem",
    sm: "1.25rem",
    lg: "1.5rem",
    xl: "1.75rem",
    xxl: "2rem",
    "3xl": "2.25rem",
    "4xl": "2.5rem",
  },

  fontWeights: {
    hairline: 100,
    thin: 200,
    light: 300,
    normal: 400,
    medium: 500,
    semibold: 600,
    bold: 700,
    extrabold: 800,
    black: 900,
  },

  fontSizes: {
    xs: "0.75rem",
    sm: "0.875rem",
    md: "1rem",
    lg: "1.125rem",
    xl: "1.25rem",
    "2xl": "1.5rem",
    "3xl": "1.875rem",
    "4xl": "2.25rem",
    "5xl": "3rem",
    "6xl": "3.75rem",
    "7xl": "4.5rem",
    "8xl": "6rem",
    "9xl": "8rem",
  },

  fonts: {
    // eslint-disable-next-line max-len
    heading: `-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
    // eslint-disable-next-line max-len
    body: `-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
    // eslint-disable-next-line max-len
    mono: `SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace`,
  },
};

export type Typography = typeof typography;
```

- Our theme values are not exactly what chakra uses, some of them are custom values.

### Theme setup

- Now under the theme folder create an index.ts file and paste

```ts
import { colors } from "./colors";
import { spacing } from "./spacing";
import { typography } from "./typography";

export const defaultTheme = {
  breakpoints: ["450px", "600px", "960px", "1280px", "1920px"],

  fontSizes: typography.fontSizes,
  fonts: typography.fonts,
  fontWeights: typography.fontWeights,

  letterSpacings: typography.letterSpacings,
  lineHeights: typography.lineHeights,

  colors,

  space: spacing,
};

export type AppTheme = typeof defaultTheme;
```

- Note that for the defaultTheme we are using object keys like `space`, `lineHeights`, etc. this is in accordance to the styled-system [api](https://styled-system.com/api).

### Setup the Provider

- Under the src folder create a new file called provider.tsx here we will pass our theme object to the `ThemeProvider` from styled-components. We will use this provider to wrap our React Apps in our own projects. Also note I am using `createGlobalStyle` to load the font-family.

```tsx
/* eslint-disable max-len */
import * as React from "react";
import { ThemeProvider, createGlobalStyle } from "styled-components";

import { defaultTheme } from "./theme";

const GlobalStyles = createGlobalStyle`
  * {
    @import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');
    padding: 0;
    margin: 0;
    box-sizing: border-box;
  }

  html {
    font-family: Montserrat;
  }

  a {
    text-decoration: none;
  }
`;

export const ChakraProvider: React.FC = ({ children }) => {
  return (
    <ThemeProvider theme={defaultTheme}>
      <GlobalStyles />
      {children}
    </ThemeProvider>
  );
};
```

- Now under the index.tsx remove the `ExampleComponent` code from the previous tutorial and just paste the following -

```tsx
export * from "./provider";
```

- Now under .storybook/preview.js import the `ChakraProvider` replace -

```js
export const decorators = [addReadme, (Story) => <Story />];
```

with -

```js
import { ChakraProvider } from "../src/provider";

export const decorators = [
  addReadme,
  (Story) => (
    <ChakraProvider>
      <Story />
    </ChakraProvider>
  ),
];
```

- Now run `npm run build`, let the build finish.

- Under the `example/src/index.tsx` we import our provider from the library and wrap our whole app with it -

```tsx
import * as React from "react";
import ReactDOM from "react-dom";
import { ChakraProvider } from "chakra-ui-clone";

import { App } from "./App";

ReactDOM.render(
  <ChakraProvider>
    <App />
  </ChakraProvider>,
  document.getElementById("root")
);
```

- Now open `example/src/App.tsx` and paste -

```tsx
import * as React from "react";

export function App() {
  return <div>Hello Chakra clone.</div>;
}
```

- Now run `npm run start`, check if the `Montserrat font` has been applied this means that our `Provider` worked.

- Also as a clean setup remove the index.stories.tsx file from the src folder.

### Summary

There you go guys in this tutorial we completed our theme setup and created and exported our `Provider` component. We wrapped our stories with the Provider. Also we wrapped our example react app under the example folder with the provider. All the theme object keys are inline with styled-system please check their docs. You can find the code for this tutorial under the **theme-setup** branch [here](https://github.com/yaldram/chakra-ui-clone). In the next tutorial we will create our first component. Until next time PEACE.