DEV Community

Arsalan Ahmed Yaldram
Arsalan Ahmed Yaldram

Posted on

Building a design system using Solidjs, Typescript, SCSS, CSS Variables and Vite - Setup

Introduction

In this tutorial series we will create a design system using SolidJs and Typescript. In my previous design system tutorial series we created a design system using React, Typescript and Scss which was an improvement over a previous design system built using React, Typescript, Styled components and Styled systems. This tutorial series goes a step further here unlike the previous tutorial series where we shipped different classes for light and dark modes - .badge {} & [data-theme="dark"] .badge {}, in this series we will use CSS Variables for implementing light and dark modes. For this tutorial series all the theme tokens, components are inspired by nextui. 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 solid-vite-lib 
cd solid-vite-lib
git init
git remote add origin https://github.com/username/repo.git
Enter fullscreen mode Exit fullscreen mode

Now after running npm init here is my package.json -

{
  "name": "@yaldram/solid-vite-lib",
  "version": "0.0.1",
  "description": "A starter project for developing a UI library using SolidJS & Typescript.",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/yaldram/solid-vite-lib.git"
  },
  "author": "Arsalan Yaldram",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/yaldram/solid-vite-lib/issues"
  },
  "homepage": "https://github.com/yaldram/solid-vite-lib#readme"
}  
Enter fullscreen mode Exit fullscreen mode

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 vite-plugin-solid
Enter fullscreen mode Exit fullscreen mode
  • 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 definition files.

Now let us install our peer dependencies from your terminal run -

yarn add -D solid-js
Enter fullscreen mode Exit fullscreen mode

Make sure you add solid-js to peerDependencies in your package.json because we don't want to bundle them with our library.

"peerDependencies": {
    "solid-js": "^1.7.3"
 }
Enter fullscreen mode Exit fullscreen mode

Finally, we install cva as a dependency -

yarn add class-variance-authority
Enter fullscreen mode Exit fullscreen mode

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": "preserve",
    "jsxImportSource": "solid-js",
    "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"]
}
Enter fullscreen mode Exit fullscreen mode

Make a note that we have added the "jsxImportSource": "solid-js".

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 solidPlugin from "vite-plugin-solid";

import { peerDependencies } from "./package.json";

export default defineConfig({
  plugins: [solidPlugin()],
  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
           */
          if (name === "style.css") {
            return "css/main.css";
          }
          return name;
        },
      },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

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"
   },
   "./main.css": {
     "import": "./dist/css/main.css"
   }
 },
 "scripts": {
    "build": "tsc && vite build"
 },
Enter fullscreen mode Exit fullscreen mode

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/solid-vite-lib".
  2. The second entry is for importing the css from our library - import "@yaldram/solid-vite-lib/main.css".

Step Three: Storybook setup

We will use the latest and greatest storybook version 7 -

npx sb@next init
Enter fullscreen mode Exit fullscreen mode

Under .storybook/preview-head.html paste the following -

<link rel="preconnect" href="https://fonts.gstatic.com" />

<style>
  * {
    padding: 0;
    margin: 0;
  }

  body {
    font-family: 'Nunito Sans', -apple-system, '.SFNSText-Regular', 'San Francisco',
      BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  }
</style>

<script>
  window.global = window;
</script>
Enter fullscreen mode Exit fullscreen mode

Under .storybook/main.ts paste the following -

import type { StorybookConfig } from "storybook-solidjs-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-solidjs-vite",
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
};
export default config;
Enter fullscreen mode Exit fullscreen mode

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

Now inside the box folder create a new file index.tsx -

import { Component, ComponentProps } from "solid-js";

export type BoxProps = ComponentProps<"div">;

export const Box: Component<BoxProps> = (props) => {
  return <div {...props} />;
};
Enter fullscreen mode Exit fullscreen mode

Create a new file for the story box.stories.tsx -

/** @jsxImportSource solid-js */

import { Box } from ".";

export default {
  title: "Atoms/Layout/Box",
};

export const Default = () => (
  <Box style="padding: 1rem; background: green; color: white; width: 100%">
    Box Component
  </Box>
);
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

Under /components/atoms/index.ts -

export * from "./layouts";
Enter fullscreen mode Exit fullscreen mode

Finally under src/index.ts paste the following -

export * from "./components/atoms";
Enter fullscreen mode Exit fullscreen mode

From the terminal, build the project by running -

yarn build
Enter fullscreen mode Exit fullscreen mode

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 Solid project. Read more on publishing here.

Step Six: Setup Eslint and Prettier

From the terminal install the following packages -

 yarn add --dev eslint eslint-plugin-solid
Enter fullscreen mode Exit fullscreen mode

Now from the terminal run - npm init @eslint/config, select none for the framework and yes for Typescript. Now lets add prettier -

 yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
Enter fullscreen mode Exit fullscreen mode

In the .eslintrc.json file -

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": ["eslint:recommended", "plugin:solid/typescript"],
  "overrides": [],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": ["solid", "prettier"],
  "rules": {
    "solid/reactivity": "error",
    "solid/no-destructure": "error",
    "solid/jsx-no-undef": "error"
  }
}
Enter fullscreen mode Exit fullscreen mode

In the .eslintignore file -

build/
dist/
node_modules/
storybook-static
.snapshots/
yarn.lock
*.min.js
*.css
*.svg
Enter fullscreen mode Exit fullscreen mode

In the .prettierrc file paste -

{
  "singleQuote": true,
  "jsxSingleQuote": true,
  "semi": false,
  "tabWidth": 2,
  "bracketSpacing": true,
  "jsxBracketSameLine": false,
  "arrowParens": "always",
  "trailingComma": "none"
}
Enter fullscreen mode Exit fullscreen mode

In the .prettierignore file paste -

build/
dist/
node_modules/
storybook-static
.snapshots/
yarn.lock
*.min.js
*.css
*.svg
Enter fullscreen mode Exit fullscreen mode

Finally, under the package.json add the following scripts -

"lint": "eslint \"./**/*\"",
"lint:fix": "eslint \"src/**/*\" --fix",
"pretty": "prettier . --write",
Enter fullscreen mode Exit fullscreen mode

Step Seven - Setup Husky hooks

From your terminal run the following -

yarn add -D husky nano-staged
npx husky install
Enter fullscreen mode Exit fullscreen mode

nano-staged is lighter and more performant than lint-staged. Now from the terminal add a husky pre-commit hook

npx husky add .husky/pre-commit "npx nano-staged"
Enter fullscreen mode Exit fullscreen mode

Finally add the following under package.json -

 "nano-staged": {
    "*.{js,jsx,ts,tsx}": "prettier --write"
  }
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this tutorial we added the base setup for 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)