Checkbox
Interactive UI Explorer
아래에서 각 컴포넌트의 다양한 Variants와 States를 문서화된 형태로 테스트해볼 수 있습니다.
Live Preview
이용약관에 동의합니다 (필수)
이메일 수신 동의
SMS 수신 동의
잠긴 옵션
Checkbox
애니메이션이 적용된 커스텀 체크박스 컴포넌트입니다.
실제 input은 숨기고 시각적 요소로 체크 상태를 표현하며, 체크 시 부드러운 애니메이션이 적용됩니다.
기본 체크박스
라벨과 함께 사용하는 기본 체크박스
Default
Live Preview
Checked
Live Preview
No Label
Live Preview
States
체크박스의 다양한 상태 (disabled, error, helper text)
Disabled
Live Preview
Checked Disabled
Live Preview
Error
Live Preview
Helper Text
선택 사항입니다.
Live Preview
Implementation
제작 코드
이 컴포넌트가 실제로 어떻게 구현되어 있는지 — 본체 .tsx 파일을 그대로 보여줍니다. variant 매핑·접근성 처리·forwardRef 패턴 등 디테일을 그대로 확인할 수 있어요.
Checkboxtypescript
"use client";
/**
* Checkbox Component
*
* Premium animated checkbox with polished interactions.
* Features smooth scaling, subtle hover ripple, and refined typography.
*/
import { InputHTMLAttributes, forwardRef, useId, useState, useEffect } from "react";
import { CheckIcon } from "@/components/Icon/check";
import { cn } from "@/lib/cn";
export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
label?: string;
error?: string;
helperText?: string;
/** 다크 배경에서 사용 시 hover 효과·텍스트 색상을 어두운 테마에 맞게 조정 */
variant?: "default" | "dark";
}
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
({ className, label, error, helperText, id, disabled, checked, onChange, variant = "default", ...props }, ref) => {
const generatedId = useId();
const checkboxId = id ?? `checkbox-${generatedId.replace(/:/g, "-")}`;
const [isChecked, setIsChecked] = useState(checked || false);
useEffect(() => {
setIsChecked(checked || false);
}, [checked]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setIsChecked(e.target.checked);
onChange?.(e);
};
const activeClasses = (checked: boolean, error?: string) => {
if (error) return "";
if (checked) return "shadow-[0_0_0_4px_rgba(15,23,42,0.06)] scale-105";
return "";
};
return (
<div className={cn("group/checkbox inline-flex flex-col items-center justify-center gap-1.5", className)}>
<label
htmlFor={checkboxId}
className={cn(
"relative flex items-center gap-3 cursor-pointer select-none px-1 py-0.5 rounded-lg transition-all duration-300",
disabled
? "opacity-40 cursor-not-allowed"
: variant === "dark"
? "hover:bg-transparent"
: "hover:bg-slate-50"
)}
>
{/* Invisible native input */}
<input
ref={ref}
type="checkbox"
id={checkboxId}
className="sr-only peer"
disabled={disabled}
checked={isChecked}
onChange={handleChange}
aria-invalid={error ? "true" : "false"}
aria-describedby={error ? `${checkboxId}-error` : helperText ? `${checkboxId}-helper` : undefined}
{...props}
/>
{/* Visual box with ripple */}
<div className="relative flex items-center justify-center shrink-0">
{/* Hover ripple */}
<div
className={cn(
"absolute inset-0 -m-2 rounded-md transition-all duration-300 scale-0 group-hover/checkbox:scale-100 group-hover/checkbox:bg-slate-200/50",
isChecked && "group-hover/checkbox:bg-slate-900/5",
disabled && "hidden"
)}
/>
{/* Outer square */}
<div
className={cn(
"w-5 h-5 rounded border-2 flex items-center justify-center transition-all duration-300",
error
? "border-rose-400 bg-rose-50/40"
: isChecked
? "border-slate-900 bg-white"
: "border-slate-300 bg-white hover:border-slate-400",
activeClasses(isChecked, error),
error
? "peer-focus-visible:ring-2 peer-focus-visible:ring-rose-500/20"
: "peer-focus-visible:ring-2 peer-focus-visible:ring-violet-500/20"
)}
>
<CheckIcon
className={cn(
"w-3 h-3 transition-all duration-200",
isChecked ? "scale-100 opacity-100" : "scale-0 opacity-0",
error ? "text-rose-600" : "text-slate-900"
)}
/>
</div>
</div>
{/* Label text */}
{label && (
<span
className={cn(
"text-sm font-medium transition-colors duration-300",
variant === "dark"
? "text-slate-200"
: isChecked ? "text-slate-900" : "text-slate-500",
disabled
? variant === "dark" ? "text-slate-400" : "text-slate-400"
: variant === "dark"
? ""
: "group-hover/checkbox:text-slate-800"
)}
>
{label}
</span>
)}
</label>
{/* Helper / error messages */}
{(error || helperText) && (
<div className="ml-[32px] space-y-1">
{error && (
<p id={`${checkboxId}-error`} className="text-[11px] font-bold text-rose-600 flex items-center gap-1 animate-in fade-in slide-in-from-left-1">
<span className="w-1 h-1 rounded-full bg-rose-600" />
{error}
</p>
)}
{helperText && !error && (
<p id={`${checkboxId}-helper`} className="text-[11px] font-medium text-slate-500 leading-tight">
{helperText}
</p>
)}
</div>
)}
</div>
);
}
);
Checkbox.displayName = "Checkbox";