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
- 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
- Under package.json -
"peerDependencies": {
"react": "^17.0.2",
"styled-components": "^5.3.0"
}
- Also install types for it -
npm install --save-dev @types/styled-components
- Then install styled-system as a dependency -
npm install styled-system
- Also install the types for it -
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
andtypography.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",
];
- 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;
- 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;
- 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;
- 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 usingcreateGlobalStyle
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>
);
};
- Now under the index.tsx remove the
ExampleComponent
code from the previous tutorial and just paste the following -
export * from "./provider";
- Now under .storybook/preview.js import the
ChakraProvider
replace -
export const decorators = [addReadme, (Story) => <Story />];
with -
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 -
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 -
import * as React from "react";
export function App() {
return <div>Hello Chakra clone.</div>;
}
Now run
npm run start
, check if theMontserrat font
has been applied this means that ourProvider
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.
Top comments (0)