Introduction
In my previous design system tutorial series taking inspiration from chakra-ui we created a design system using React, Typescript, styled-components and styled-system. But css-in-js libraries like styled-components have a performance penalty, and one should avoid using css-in-js libraries if you are using Server Side Rendering. In this tutorial series we will re-create our design system but this time using React, Typescript, Scss and class-variance-authority. I would encourage you to play around with the deployed storybook. All the code for this series is available on GitHub.
Step One: Bootstrap the component library
I won't go into the details; I would request you to check my other tutorial on creating a component library from scratch using Vite. From your terminal -
mkdir react-scss-chakra-ui
cd react-scss-chakra-ui
git init
git remote add origin https://github.com/username/repo.git
Now after running npm init here is my package.json -
{
"name": "@yaldram/react-scss-chakra-ui",
"version": "0.0.1",
"description": "A chakra clone design-system using react, scss, class-variance-authority, only static styles, no css in js.",
"source": "src/index.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/yaldram/react-scss-chakra-ui.git"
},
"keywords": [
"react",
"scss",
"class-variance-authority"
],
"author": "Arsalan Yaldram.",
"license": "ISC",
"bugs": {
"url": "https://github.com/yaldram/react-scss-chakra-ui/issues"
},
"homepage": "https://github.com/yaldram/react-scss-chakra-ui#readme",
}
Now lets install our dev dependencies, for building and bundling our project. We require the following -
yarn add -D typescript @types/node sass vite @rollup/plugin-typescript tslib
- We are installing
sass
the complier for scss. - We are installing
vite
which will build and bundle our library. - We are installing
@rollup/plugin-typescript & tslib
for generating the type def files.
Now let us install our peer dependencies from your terminal run -
yarn add -D react react-dom @types/react @types/react-dom
Make sure you add react and react-dom to peerDependencies
in your package.json because we don't want to bundle them with our library.
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
Finally we install cva as a dependency -
yarn add class-variance-authority
Step Two: Setup vite
Now from the root of our project create a tsconfig.json
-
{
"compilerOptions": {
"outDir": "dist",
"module": "esnext",
"target": "ESNext",
"lib": ["dom", "esnext"],
"moduleResolution": "node",
"jsx": "react",
"sourceMap": true,
"declaration": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.test.tsx"]
}
Again from the root of our project create a new file vite.config.ts
-
import { defineConfig } from "vite";
import typescript from "@rollup/plugin-typescript";
import { resolve } from "path";
import { peerDependencies } from "./package.json";
export default defineConfig({
build: {
outDir: "dist",
sourcemap: true,
lib: {
entry: resolve(__dirname, "src/index.ts"),
formats: ["es"],
fileName: "index",
},
rollupOptions: {
external: [...Object.keys(peerDependencies)],
plugins: [typescript({ tsconfig: "./tsconfig.json" })],
output: {
assetFileNames: ({ name = "" }) => {
/**
* Vite gives us a single style.css file
* in the build we are moving it to the css
* folder and renaming it to main.css
*/
return name === "style.css" ? "css/main.css" : name;
},
},
},
},
});
Finally, we need to make some changes under the package.json
file -
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"source": "src/index.ts",
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/index.mjs"
},
"./dist/css/main.css": {
"import": "./dist/css/main.css"
}
},
"scripts": {
"build": "tsc && vite build"
},
Take a note of the exports
entry in the package.json file : -
- The first
.
entry is for importing our componentsimport { Box } from "@yaldram/react-scss-chakra-ui"
. - The second entry is for importing the css from our library -
import "@yaldram/react-scss-chakra-ui/dist/css/main.css"
.
Step Three: Storybook setup
We will use the latest and greatest storybook version 7 -
npx sb@next init
Under .storybook/preview-head.html
paste the following -
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;900&display=swap"
rel="stylesheet"
/>
<style>
* {
padding: 0;
margin: 0;
}
body {
font-family: Montserrat, sans-serif;
}
</style>
<script>
window.global = window;
</script>
Under .storybook/main.ts
paste the following -
import type { StorybookConfig } from "@storybook/react-vite";
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: true,
},
};
export default config;
Finally under .storybook
folder rename the preview.ts
file to preview.tsx
.
Step Five: Folder structure
While building components, I follow the atomic design methodology. Create a src
folder, inside it create a new file index.ts
. Now create a components
folder, create 2 folders inside it atoms
& molecules
.
Now inside the atoms folder create a index.ts
file, and create a new folder layouts
. Finally, inside layouts
create an index.ts
file and create 2 folders box
& flex
. Your directory structure should look like
Now inside the box
folder create a new file index.tsx
-
import * as React from "react";
export interface BoxProps extends React.ComponentPropsWithoutRef<"div"> {
children?: React.ReactNode;
}
export function Box(props: BoxProps) {
return <div {...props} />;
}
Create a new file for the story box.stories.tsx
-
import * as React from "react";
import { Box } from ".";
export default {
title: "Atoms/Layout/Box",
};
export const Default = () => <Box>This is a Box Component</Box>;
Now from your terminal run yarn storybook
and check the output.
Let's export our Box component under components/atoms/layouts/index.ts
-
export * from "./box";
Under /components/atoms/index.ts
-
export * from "./layouts";
Finally under src/index.ts
paste the following -
export * from "./components/atoms";
From the terminal, build the project by running -
yarn build
It should output a dist
folder with an index.mjs
file along with our typescript definition files. You can also test our library by publishing it and importing the Box
component in a new React project. Read more on publishing here.
Conclusion
In this tutorial we added the base setup of our design system library. All the code for this tutorial can be found here. In the next tutorial we will work with scss adding our design tokens (colors, spacing, line-height values). Until next time PEACE.
Top comments (0)