Building a design system with dark mode using React, Typescript, scss, cva and Vite - Setup
Tags: react, css, typescript, vite
Published: 2023-03-20 13:19:54

### Introduction
In my previous design system [tutorial series](https://dev.to/yaldram/working-with-styled-system-using-typescript-4mkj) taking inspiration from [chakra-ui](https://chakra-ui.com/) 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](https://www.npmjs.com/package/class-variance-authority). I would encourage you to play around with the deployed [storybook](http://yaldram-react-scss-chakra-ui.s3-website.ap-south-1.amazonaws.com/?path=/story/atoms-forms-button--default). All the code for this series is available on [GitHub](https://github.com/yaldram/react-scss-chakra-ui).

### Step One: Bootstrap the component library
I won't go into the details; I would request you to check my other [tutorial](https://dev.to/yaldram/react-component-library-setup-with-vite-typescript-styled-components-styled-system-and-storybook-55ad) on creating a component library from scratch using Vite. From your terminal - 
```bash
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 - 
```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 - 
```bash
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 - 
```bash
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.
```json
"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` - 
```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` - 
```typescript
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 - 
```json
 "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 : - 
1. The first `.` entry is for importing our components `import { Box } from "@yaldram/react-scss-chakra-ui"`. 
2. 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 -
```bash
npx sb@next init
```
Under `.storybook/preview-head.html` paste the following - 
```html
<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 - 
```typescript
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

![directory-structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y92sks4zqfwocfyaiclx.png)


Now inside the `box` folder create a new file `index.tsx` - 
```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` - 
```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` -
```ts
export * from "./box";
```

Under `/components/atoms/index.ts` - 
```ts
export * from "./layouts";
```

Finally under `src/index.ts` paste the following - 
```ts
export * from "./components/atoms";
```
From the terminal, build the project by running - 
```bash
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](https://dev.to/yaldram/react-component-library-setup-with-vite-typescript-styled-components-styled-system-and-storybook-55ad).

### Conclusion 
In this tutorial we added the base setup of our design system library. All the code for this tutorial can be found [here](https://github.com/yaldram/react-scss-chakra-ui/tree/feat/add-storybook). In the next tutorial we will work with scss adding our design tokens (colors, spacing, line-height values). Until next time PEACE.