크기
sm · md
상태
unchecked · checked · indeterminate · disabled
접근성 기준
44px hit target + visible label + aria-checked=mixed (indeterminate 시)

기본

interactive (라이브 토글 · description sibling)

입장 전 필수 확인 항목입니다.

코드 보기tsx
import { Checkbox } from "@olundot/ui";

export function PolicyCheckbox() {
  return (
    <Checkbox
      name="confirm"
      label="시험 정책을 확인했습니다."
      description="입장 전 필수 확인 항목입니다."
    />
  );
}

체크리스트

interactive (체크리스트 라이브 토글 · 비활성 포함)

응시 직후 즉시 전송됩니다.

기관 정책으로 잠겨 있습니다.

코드 보기tsx
import { Checkbox } from "@olundot/ui";

export function FilterChecklist() {
  return (
    <div style={{ display: "grid", gap: "12px" }}>
      <Checkbox name="f1" label="자동 제출" defaultChecked />
      <Checkbox name="f2" label="결과 메일 발송" description="응시 직후 즉시 전송됩니다." />
      <Checkbox name="f3" label="외부 공유" disabled description="기관 정책으로 잠겨 있습니다." />
    </div>
  );
}

상태

라이브 토글 (off · on · indeterminate 3-cycle) · disabled
코드 보기tsx
import { useState } from "react";
import { Checkbox, type CheckboxCheckedState } from "@olundot/ui";

// off/on은 uncontrolled defaultChecked로 라이브 토글, indeterminate는 controlled
// 3-cycle (off → indeterminate → on → off) — 사용자가 클릭하며 3 상태 학습.
// disabled 2개는 정적 (비활성은 상호작용 X).
export function CheckboxStates() {
  const [tri, setTri] = useState<CheckboxCheckedState>("indeterminate");
  return (
    <div style={{ display: "grid", gap: "12px" }}>
      <Checkbox name="off" label="기본 (off)" />
      <Checkbox name="on" label="선택됨" defaultChecked />
      <Checkbox
        name="mix"
        label="부분 선택 (클릭하면 3-cycle)"
        checked={tri}
        onCheckedChange={(next) => {
          // 3-cycle: indeterminate → on → off → indeterminate
          if (tri === "indeterminate") setTri(true);
          else if (tri === true) setTri(false);
          else setTri("indeterminate");
          void next; // Radix가 자동 토글하려 하지만 우리가 cycle 강제
        }}
      />
      <Checkbox name="d-off" label="비활성 off" disabled />
      <Checkbox name="d-on" label="비활성 on" defaultChecked disabled />
    </div>
  );
}

필수 동의 + 검증

라이브 (* marker · submit 검증 · error 노출)

등록 전 필수 확인입니다.

코드 보기tsx
import { useState } from "react";
import { Button, Checkbox } from "@olundot/ui";

// required prop → label 옆 * marker 자동 (aria-hidden, Input/Select 정합).
// submit 미체크 시 error prop 노출 — description 자리에 빨강 메시지 표시 (a11y 표준).
// 체크 시 error 즉시 해제 (FormField 패턴과 동일한 description ↔ error 교체).
export function CheckboxRequiredLive() {
  const [agreed, setAgreed] = useState(false);
  const [error, setError] = useState<string | undefined>();
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!agreed) setError("필수 동의 항목입니다.");
    else setError(undefined);
  };
  return (
    <form onSubmit={handleSubmit} style={{ display: "grid", gap: "12px" }}>
      <Checkbox
        name="agree"
        label="시험 정책에 동의합니다."
        description="등록 전 필수 확인입니다."
        required
        checked={agreed}
        onCheckedChange={(c) => { setAgreed(c === true); if (c) setError(undefined); }}
        error={error}
      />
      <Button type="submit">제출</Button>
    </form>
  );
}