This document outlines the steps to create Checkbox
component styled withTailwind CSS and using some npm dependency libraries.
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.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
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