DEV Community

Arsalan Ahmed Yaldram
Arsalan Ahmed Yaldram

Posted on

Theme setup for styled components & styled system

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, 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. All the code for this tutorial can be found under the theme-setup branch here.

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 -
git checkout -b theme-setup
Enter fullscreen mode Exit fullscreen mode
  • 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.
npm install --save-dev styled-components
Enter fullscreen mode Exit fullscreen mode
  • Under package.json -
"peerDependencies": {
    "react": "^17.0.2",
    "styled-components": "^5.3.0"
  }
Enter fullscreen mode Exit fullscreen mode
  • Also install types for it -
npm install --save-dev @types/styled-components
Enter fullscreen mode Exit fullscreen mode
  • Then install styled-system as a dependency -
npm install styled-system
Enter fullscreen mode Exit fullscreen mode
  • Also install the types for it -
npm install --save-dev @types/styled-system
Enter fullscreen mode Exit fullscreen mode

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.

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

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",
];
Enter fullscreen mode Exit fullscreen mode
  • Similarly under the spacing.ts file paste -
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;
Enter fullscreen mode Exit fullscreen mode
  • And for typography.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;
Enter fullscreen mode Exit fullscreen mode
  • 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
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;
Enter fullscreen mode Exit fullscreen mode
  • Note that for the defaultTheme we are using object keys like space, lineHeights, etc. this is in accordance to the styled-system 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.
/* 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>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • Now under the index.tsx remove the ExampleComponent code from the previous tutorial and just paste the following -
export * from "./provider";
Enter fullscreen mode Exit fullscreen mode
  • Now under .storybook/preview.js import the ChakraProvider replace -
export const decorators = [addReadme, (Story) => <Story />];
Enter fullscreen mode Exit fullscreen mode

with -

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

export const decorators = [
  addReadme,
  (Story) => (
    <ChakraProvider>
      <Story />
    </ChakraProvider>
  ),
];
Enter fullscreen mode Exit fullscreen mode
  • 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 -

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")
);
Enter fullscreen mode Exit fullscreen mode
  • Now open example/src/App.tsx and paste -
import * as React from "react";

export function App() {
  return <div>Hello Chakra clone.</div>;
}
Enter fullscreen mode Exit fullscreen mode
  • 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. In the next tutorial we will create our first component. Until next time PEACE.

Discussion (0)