This document outlines the steps to create Radio
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// radio.helpers.ts
2import { cva } from "class-variance-authority";
3
4export const radioVariants = cva(
5 "w-6 h-6 rounded-full flex justify-center items-center border text-white bg-white",
6 {
7 variants: {
8 inputSize: {
9 small: "w-5 h-5 border data-[checked=true]:border-[6px]",
10 medium: "w-6 h-6 border-2 data-[checked=true]:border-[8px]",
11 large: "w-8 h-8 border-[3px] data-[checked=true]:border-[10px]",
12 },
13 radioColor: {
14 primary: "border-blue-500 data-[disabled=true]:border-blue-300",
15 secondary: "border-gray-500 data-[disabled=true]:border-gray-300",
16 success: "border-green-500 data-[disabled=true]:border-green-300",
17 danger: "border-red-500 data-[disabled=true]:border-red-300",
18 warning: "border-yellow-500 data-[disabled=true]:border-yellow-300",
19 info: "border-cyan-500 data-[disabled=true]:border-cyan-300",
20 light: "border-gray-300 data-[disabled=true]:border-gray-200",
21 dark: "border-black data-[disabled=true]:border-gray-500",
22 },
23 },
24 defaultVariants: {
25 inputSize: "medium",
26 radioColor: "primary",
27 },
28 },
29);
30
31export const labelVariants = cva(
32 "cursor-pointer select-none pl-2 font-medium",
33 {
34 variants: {
35 inputSize: {
36 small: "text-xs",
37 medium: "text-sm",
38 large: "text-lg",
39 },
40 },
41 defaultVariants: {
42 inputSize: "medium",
43 },
44 },
45);
46
1// radio.component.tsx
2import clsx from "clsx";
3import { useId } from "react";
4import type { VariantProps } from "class-variance-authority";
5import { labelVariants, radioVariants } from "./radio.helpers";
6
7export interface RadioProps
8 extends React.InputHTMLAttributes<HTMLInputElement>,
9 VariantProps<typeof radioVariants> {
10 checked: boolean;
11 label?: string;
12 className?: string;
13 onChangeChecked?: (
14 checked: boolean,
15 value: readonly string[] | string | number | undefined,
16 ) => void;
17}
18
19const Radio: React.FC<RadioProps> = ({
20 radioColor,
21 inputSize,
22 value,
23 label,
24 checked,
25 disabled,
26 className,
27 onChangeChecked,
28 ...props
29}) => {
30 const id = useId();
31
32 return (
33 <div className={cn("flex items-center", className)}>
34 <div className="relative cursor-pointer">
35 <div
36 data-disabled={Boolean(disabled).toString()}
37 data-checked={Boolean(checked).toString()}
38 className={radioVariants({ inputSize, radioColor })}
39 />
40 <input
41 {...props}
42 type="radio"
43 id={id}
44 disabled={disabled}
45 checked={checked}
46 onChange={(e) => onChangeChecked?.(e.target.checked, value)}
47 className="absolute inset-0 cursor-pointer opacity-0"
48 />
49 </div>
50 {label && (
51 <label htmlFor={id} className={labelVariants({ inputSize })}>
52 {label}
53 </label>
54 )}
55 </div>
56 );
57};
58
59export default Radio;
60