Building and Publishing Design Systems | Part 2

This post is part of the "Building design system and micro frontends" series. See the first post for context. Source code for all the parts of this series can be found in this GitHub repository.

1. Building design system and micro frontends

2. Building and publishing design system (you are here)

3. Building micro frontend consuming design system

4. Building shell application for micro frontends

For building our design system, we will use react, tailwind, storybook and webpack.

A few words why I selected this technologies:

  • React - proven, stable, excellent component composition system, focus on back-compatibility. Read the "Design Principles" post on reacts blog; this post outlines react development guiding principles.

  • Tailwind - gives us a simple way to define seed for the design system. Sizes/Distances scale, colour palettes, shadows, responsive breakpoints, transitions and animations etc., all can be defined succinctly. It also gives us ultimate style isolation and a big productivity boost.

  • Storybook - helps us develop components in isolation. Helps in component testing and, when exported, delivers portable documentation.

  • Webpack - proven, stable, vivid plugin ecosystem; a lot of materials.


Setting up Design System Project

Create directory design-system and run the following commands from it:

npm init -y will setup package.json file.

Run npm run storybook to build and run storybook. It should bind to port 6006. Open http://localhost:6006. You should see some example stories.

The npx sb init command takes care of setting up the storybook. It will create a stories directory with examples in the project root directory. You can remove its content. We will create a simple story later from scratch.

Set up tailwind

npm i -D tailwindcss@2.2.4  postcss@8.3.5
npx tailwindcss init

npx tailwindcss init will create ./tailwind.config.js file with the default configuration.

Open ./tailwind.config.js Add mode property and set purge patterns (lines 2,3).

module.exports = {
  mode: "jit",
  purge: ["./src/**/*.js", "./src/**/*.html"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: 
  ,
  variants: {
    extend: 
  ,
  plugins: [],
}

Create ./postcss.config.js in root directory.

Paste following code in ./postcss.config.js:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {}
  
}

This configuration file adds tailwindcss and autoprefixer plugins to PostCss processing pipeline.

Setup tailwind for storybook

In the next two steps we will setup tailwind for storybook. We need to install appropriate plugin and configure it.

Install @storybook/addon-postcss addon for storybook.

npm i -D @storybook/addon-postcss@2.0.0

Update ./.storybook/main.js; add highlighted configuration to addons array:

module.exports = {
  stories: [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    {
      name: "@storybook/addon-postcss",
      options: {
        postcssLoaderOptions: {
          implementation: require("postcss"),
        },
      },
    }
  ],
  core: {
    builder: "webpack5",
  },
};

Create file ./src/styles.css.

Paste following code in ./src/styles.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Open ./.storybook/preview.js and add import styles (line 1).

import '../src/styles.css'

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
}

Create component and story

Create Button.js file in ./src/components with the following content

import React from "react";

export default function Button({label, disabled, children}) {
  return (
    <button className={`
    inline-flex items-center px-4 py-2
    text-white text-base  font-bold
    bg-blue-600 
    border border-transparent rounded-sm
    
    hover:bg-blue-500 hover:shadow-sm 
    
    disabled:bg-gray-200 disabled:hover:shadow-none disabled:cursor-not-allowed disabled:text-gray-400

    focus:outline-none focus:ring-2 focus:ring-offset-2 
    `} disabled= ></button>
  );
}

In ./stories directory create `Button.stories.js` file with the following content:

import React from 'react';

import Button from '../src/components/Button';

export default {
    title: 'Example/Button',
    component: Button,
    argTypes: {
        label: {control: 'text'},
        disabled: {control: 'boolean'}
    },
};

const Template = (args) => <Button  />;

export const Primary = Template.bind({});
Primary.args = {
    label: 'Button',
    disabled: false
};

export const DisabledButton = Template.bind({});
DisabledButton.args = {
    label: 'Button',
    disabled: true
};


Run npm run storybook. Open http://localhost:6006. You should see something like this:

Building design system package

It is time to create production build of our design system. It should contain only necessary code. We will exclude react from the build. The assumption here is that whoever is consuming design system will be responsible for including react.

In a real project design system should be packaged and published to a npm repository so the other project can use npm to get it. However, for this walkthrough, we will build it and keep it in a directory on the disk. Other projects that we build in the next post will consume it from the disk.

Create an entry point for our library; Create ./src/index.js and add the following content:

import './styles.css'
import Button from "./components/Button";

export   

Every component in our design system must be imported and re-exported in this file.

Install following packages:

npm i -D webpack@5.38.1 webpack-cli@4.7.2  @babel/preset-env@7.14.4 @babel/preset-react@7.13.13 babel-loader@8.2.2 cross-env@7.0.3 css-loader@5.2.6 mini-css-extract-plugin@2.1.0 css-minimizer-webpack-plugin@3.0.2 postcss-loader@5.3.0 style-loader@2.0.0

Create file ./.babelrc with the following content:

{
  presets: [
    [
      '@babel/preset-env',
      {
        modules: false
      }
    ],
    '@babel/preset-react'
  ],
  plugins: []
}

Create file ./webpack.config.js with the following content:

const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const config = {
  entry: [
    './src/index.js'
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    library: {
      name: "fitness",
      type: "umd"
    }
  },
  externals: {
    "react": "react",
    "react-dom": "react-dom"
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'
        ]
      }
    ]
  },
  resolve: {
    extensions: [
      '.js',
      '.jsx',
      '.css'
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css'
    })
  ],
  optimization: {
    minimizer: [
      new CssMinimizerPlugin()
    ]
  }
};

module.exports = (env, argv) => {
  config.output.filename = '[name].js';
  return config;
};

There is a lot of things going on in this file; so lets break it out:

  • Line 6 - we define an entry point for our library, all components exported in this file will be available for consumers.

  • Line 9 - the output of the build will be saved to dist folder

  • Lines 11-14 - we configure webpack to create a library and use UMD for modules.

  • Lines 16-19 - we externalize react. This way it is not packaged with the library.

  • Lines 20-41 - standard configuration for building js, jsx and CSS

  • Lines 42-46 - add plugin for CSS extractions

  • Lines 47-52 - configure CSS minification

The last step is to add npm script; add following line to package.json scripts array:

"build-design-system": "cross-env NODE_ENV=production webpack --mode production"

To build the design system run npm run build-design-system.

There should be two files in the dist directory. main.js (5KB), and main.css (7KB).

Zurück
Zurück

Composite UI with Design System and Micro Frontends

Weiter
Weiter

Building Shell Application for Micro Frontend | PART 4