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
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"
}
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
- 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
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"
}
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": "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"]
}
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;
},
},
},
},
});
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"
},
Take a note of the exports
entry in the package.json file : -
- The first
.
entry is for importing our componentsimport { Box } from "@yaldram/solid-vite-lib"
. - 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
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>
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;
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 { Component, ComponentProps } from "solid-js";
export type BoxProps = ComponentProps<"div">;
export const Box: Component<BoxProps> = (props) => {
return <div {...props} />;
};
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>
);
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 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
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
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"
}
}
In the .eslintignore
file -
build/
dist/
node_modules/
storybook-static
.snapshots/
yarn.lock
*.min.js
*.css
*.svg
In the .prettierrc file paste -
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": false,
"tabWidth": 2,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"trailingComma": "none"
}
In the .prettierignore file paste -
build/
dist/
node_modules/
storybook-static
.snapshots/
yarn.lock
*.min.js
*.css
*.svg
Finally, under the package.json
add the following scripts -
"lint": "eslint \"./**/*\"",
"lint:fix": "eslint \"src/**/*\" --fix",
"pretty": "prettier . --write",
Step Seven - Setup Husky hooks
From your terminal run the following -
yarn add -D husky nano-staged
npx husky install
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"
Finally add the following under package.json -
"nano-staged": {
"*.{js,jsx,ts,tsx}": "prettier --write"
}
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)