UI Design System - Badge Component

Jazim Abbas - Jul 30 - - Dev Community

FYI: The above mentioned YouTube Video is in Hindi Language.

If you want to get a frontend job, you need to master how to build a Design System from scratch or atleast you know how to build it. Because almost every company is trying to build a design system these days. So in this series we'll exactly build this from scratch.

Tech Stack

We'll be using the following tech stack to build the Design System:

  • React
  • Storybook
  • Rollup
  • TypeScript
  • Framer Motion
  • Styled-Components
  • xStyled
  • Shadcn

Reasons to Choose the Above Tech Stack

React will always be my first priority. We need to showcase our component library to other developers or community, for that we'll be using Storybook. There are multiple options for bundling the package e.g. tsup, webpack etc, but these days Rollup is very popular so I am choosing that one and to be very honest I am very comfortable using this bundler so that's why I choose Rollup over other JavaScript bundlers. We need animations for some components, we can build all those animations from scratch or just using css but sometimes by just using css we make our life hell so I am choosing Framer Motion for that.

Now how we style our components that's the main point, we have couple of options:

  1. we can use pure CSS for that but its hard to manage
  2. tailwind css - but I am not comfortable writing it - so skipping it
  3. CSS Modules - its hard to maintain I guess
  4. CSS in JS - we are using this one

There are multiple CSS in JS libraries as well e.g. Emotion, Styled-Components etc.. I picked Styled-Components that's what I am using it for past few years. But its very hard to setup theme, we can do that setup but we have to create it from scratch e.g. Theme, Theme Tokens etc.. For that we'll be using xStyled. xStyled built by the same team that build styled-components.

Source Code

If you want the Source Code, you can use this GitHub Repository, all the code will be there.

Badge Component

Image description

This is what we are trying to build it.

Image description

And this is what we've built. This is the Final Preview.

Types

First we need to decide what props we should use, in our case we'll be using the following:

  1. BadgeSize
  2. BadgeColor
  3. Any JSX Content i.e. children
export type BadgeSize = "sm" | "md" | "lg";

export type BadgeColor = "neutral" | "error" | "warning" | "success" | "primary";

type Props = {
  children: React.ReactNode;
  size?: BadgeSize;
  color?: BadgeColor;
};
Enter fullscreen mode Exit fullscreen mode

Badge Styles

Next we need to define all the styles related to Badge

"use client";
import styled, { css } from "@xstyled/styled-components";
import { BadgeColor, BadgeSize } from "./types";

const badgeSizes = {
  sm: css`
    padding: 2px 6px;
    font-size: 0.75rem;
  `,
  md: css`
    padding: 2px 8px;
    font-size: 0.875rem;
  `,
  lg: css`
    padding: 4px 10px;
    font-size: 0.875rem;
  `,
};

const badgeColors = {
  neutral: css`
    border: 1px solid #e6e6e6;
    background-color: #f9fafb;
    color: #525252;
  `,
  error: css`
    border: 1px solid #fecaca;
    background-color: #fef2f2;
    color: #dc2626;
  `,
  warning: css`
    border: 1px solid #fde68a;
    background-color: #fffbeb;
    color: #b45309;
  `,
  success: css`
    border: 1px solid #bbf7d0;
    background-color: #f0fdf4;
    color: #15803d;
  `,
  primary: css`
    border: 1px solid #c7d2fe;
    background-color: #eef2ff;
    color: #4338ca;
  `,
};

export const Badge = styled.span<{ $size: BadgeSize; $color: BadgeColor }>`
  text-align: center;
  border-radius: 9999px;

  ${(p) => badgeSizes[p.$size]};
  ${(p) => badgeColors[p.$color]};
`;
Enter fullscreen mode Exit fullscreen mode

As you can see, in the top I added the use client directive. We are marking at client component otherwise you'll get an error if you are using nextjs.

For BadgeSize, I added the styles for each variants separately so that we can easily maintain it. And I did same for BadgeColor. And in the last I added the common styles that should use in the all the variants, I added in the main Badge Styles. Also I am conditionally extract styles from the badgeSizes and badgeColors.

Badge Component

Now I just need to render the Badge Component. Here's how I am rendering it:

import { forwardRef } from "react";
import { Badge as BadgeStyle } from "./styles";
import { BadgeColor, BadgeSize } from "./types";

type Props = {
  children: React.ReactNode;
  size?: BadgeSize;
  color?: BadgeColor;
};

const Badge = forwardRef(function Badge(
  { children, size = "md", color = "neutral" }: Props,
  ref: any,
) {
  return <BadgeStyle ref={ref} $size={size} $color={color} children={children} />;
});

export { Badge };
Enter fullscreen mode Exit fullscreen mode

Its best practice to wrap your component with the ref so that the developer who is using your component library doesn't need to wrap your component with another component just for ref. Because in many cases, we need to pass ref e.g. tooltip. I am also delegating all the rest props to the Badge Component so that developer can pass any additional props if they want.

StoryBook

Last but not least, we need to add StoryBook for our Badge Component so that we can showcase it easily and we also want to test it. Here's how you can create a Badge Component StoryBook:

import type { Meta, StoryObj } from "@storybook/react";
import { Badge } from "../components/Badge";

const meta: Meta<typeof Badge> = {
  title: "Badge",
  component: Badge,
  tags: ["autodocs"],
  parameters: {
    layout: "fullscreen",
  },
  decorators: [
    (story) => (
      <div style={{ display: "flex", justifyContent: "center", margin: "20px" }}>{story()}</div>
    ),
  ],
};

export default meta;
type Story = StoryObj<typeof meta>;

export const General: Story = {
  args: {
    children: "Badge",
  },
};
Enter fullscreen mode Exit fullscreen mode

For each individual story, you need to named export it whatever you like. You can create as much stories as you want. So that's pretty much it.


Conclusion

Its always a good idea to build a design system from scratch so that you know how design system actually built and how much developers are putting efforts into it. And by the time, you'll learn it how fun it is to make a design system.

FYI, all the components that I'll build in this series comes from the GreatFrontend UI Designs.

. . . .
Terabox Video Player