Search

Ctrl + K

Checkbox

This document outlines the steps to create Checkbox component styled withTailwind CSS and using some npm dependency libraries.

Prerequisites

  • Node.js and npm installed on your machine.
  • Tailwind CSS installed in your project.
  • CVA(class-variance-authority) is a utility for managing CSS class names based on various conditions.
  • clsx is a tiny utility for constructing className strings conditionally.
Step 1: Create variant styles
1// checkbox.helpers.ts
2import { cva } from "class-variance-authority";
3
4export const checkboxVariants = cva(
5  "rounded-md flex justify-center items-center border text-white bg-white data-[disabled=true]:bg-gray-200",
6  {
7    variants: {
8      inputSize: {
9        small: "w-5 h-5",
10        medium: "w-6 h-6",
11        large: "w-8 h-8",
12      },
13      checkboxColor: {
14        primary: [
15          "border-blue-500 data-[disabled=true]:border-blue-400 data-[disabled=true]:data-[checked=true]:border-blue-300",
16          "data-[checked=true]:bg-blue-500 data-[checked=true]:data-[disabled=true]:bg-blue-300",
17        ],
18        secondary: [
19          "border-gray-500 data-[disabled=true]:border-gray-400 data-[disabled=true]:data-[checked=true]:border-gray-300",
20          "data-[checked=true]:bg-gray-500 data-[checked=true]:data-[disabled=true]:bg-gray-300",
21        ],
22        success: [
23          "border-green-500 data-[disabled=true]:border-green-400 data-[disabled=true]:data-[checked=true]:border-green-300",
24          "data-[checked=true]:bg-green-500 data-[checked=true]:data-[disabled=true]:bg-green-300",
25        ],
26        danger: [
27          "border-red-500 data-[disabled=true]:border-red-400 data-[disabled=true]:data-[checked=true]:border-red-300",
28          "data-[checked=true]:bg-red-500 data-[checked=true]:data-[disabled=true]:bg-red-300",
29        ],
30        warning: [
31          "border-yellow-500 data-[disabled=true]:border-yellow-400 data-[disabled=true]:data-[checked=true]:border-yellow-300",
32          "data-[checked=true]:bg-yellow-500 data-[checked=true]:data-[disabled=true]:bg-yellow-300",
33        ],
34        info: [
35          "border-cyan-500 data-[disabled=true]:border-cyan-400 data-[disabled=true]:data-[checked=true]:border-cyan-300",
36          "data-[checked=true]:bg-cyan-500 data-[checked=true]:data-[disabled=true]:bg-cyan-300",
37        ],
38        light: [
39          "border-gray-300 data-[disabled=true]:border-gray-200",
40          "data-[checked=true]:bg-gray-300 data-[checked=true]:data-[disabled=true]:bg-gray-300",
41        ],
42        dark: [
43          "border-black data-[disabled=true]:border-gray-700",
44          "data-[checked=true]:bg-black data-[checked=true]:data-[disabled=true]:bg-gray-700",
45        ],
46      },
47    },
48    defaultVariants: {
49      inputSize: "medium",
50      checkboxColor: "primary",
51    },
52  },
53);
54
55export const iconVariants = cva(null, {
56  variants: {
57    inputSize: {
58      small: "w-3 h-3",
59      medium: "w-4 h-4",
60      large: "w-6 h-6",
61    },
62  },
63  defaultVariants: {
64    inputSize: "medium",
65  },
66});
67
68export const labelVariants = cva("select-none ml-2 font-medium", {
69  variants: {
70    inputSize: {
71      small: "text-xs",
72      medium: "text-sm",
73      large: "text-lg",
74    },
75  },
76  defaultVariants: {
77    inputSize: "medium",
78  },
79});
80
Step 2: Create Checkbox component
1// checkbox.component.tsx
2"use client";
3
4import clsx from "clsx";
5import { CheckIcon } from "lucide-react";
6import { useId } from "react";
7import {
8  checkboxVariants,
9  iconVariants,
10  labelVariants,
11} from "./checkbox.helpers";
12
13export interface CheckboxProps
14  extends React.InputHTMLAttributes<HTMLInputElement>,
15    VariantProps<typeof checkboxVariants> {
16  checked: boolean;
17  label?: string;
18  wrapperClassName?: string;
19  labelClassName?: string;
20  onChangeChecked?: (checked: boolean) => void;
21}
22
23const Checkbox: React.FC<CheckboxProps> = ({
24  checkboxColor,
25  inputSize,
26  label,
27  checked,
28  disabled,
29  wrapperClassName,
30  labelClassName,
31  onChangeChecked,
32  ...props
33}) => {
34  const id = useId();
35
36  return (
37    <div className={clsx("flex cursor-pointer items-center", wrapperClassName)}>
38      <div className="relative">
39        <div
40          data-disabled={Boolean(disabled).toString()}
41          data-checked={Boolean(checked).toString()}
42          className={checkboxVariants({ inputSize, checkboxColor })}
43        >
44          <CheckIcon
45            className={clsx(iconVariants({ inputSize }), { invisible: !checked })}
46          />
47        </div>
48        <input
49          {...props}
50          type="checkbox"
51          id={id}
52          disabled={disabled}
53          checked={checked}
54          onChange={(e) => onChangeChecked && onChangeChecked(e.target.checked)}
55          className="absolute inset-0 opacity-0"
56        />
57      </div>
58      {label && (
59        <label
60          htmlFor={id}
61          className={clsx(labelVariants({ inputSize }), labelClassName)}
62        >
63          {label}
64        </label>
65      )}
66    </div>
67  );
68};
69
70export default Checkbox;
71