Files
HMDesign/docs/components/checkbox.md
T

8.8 KiB
Raw Blame History

Checkbox

현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Checkbox 고유의 API·Token·상태·결정만 기록한다.

1. 상태

항목 내용
구현 상태 구현 완료 (현재 HM 규격)
기준일 2026-10-02
기반 shadcn/ui Checkbox (Radix Checkbox)
대표 Story Components/Form/Checkbox → All (이름 "Checkbox")
보류 아래 §11

2. 적용 근거

  • 참고 근거: ui-viz 기존 테스트 규격에는 Checkbox 문서가 없다(border·radius·indicator는 TBD, mark는 native UA 소유, HOLD). 구현 값으로 쓸 수 있는 확정 규격이 없어 참고만 했다.
  • 현재 HM 규격: 아래 §3~§4. 현재 HM Semantic 체계(surface, stroke, action-primary, state-active, on-state-active)로 새로 정했다.
  • 구현 방식: shadcn / Radix Checkbox 구조(Root/Indicator, data-state, indeterminate, 키보드, Label 연결, 폼 동작)를 유지하고 theme CSS로 HM 스타일을 연결.
  • 보류: §11.

3. 기본 규격 (현재 HM 규격)

항목 값 비고
Box 18 × 18px 현재 HM
Border 1px --hm-sem-stroke-default Semantic
Radius --hm-radius-control-md (4px) 공통 control radius
Face --hm-sem-bg-surface Semantic
Indicator shadcn CheckIcon / MinusIcon, 14px, --hm-sem-on-state-active shadcn 아이콘 유지
Hit area 18px box + 연결된 Label 클릭 별도 확장 없음

shadcn(lucide) 아이콘을 유지했다. 별도 HM glyph는 만들지 않았다.

4. 상태 규격

상태 값
Unchecked 위 기본 규격
Checked (data-state="checked") border·face --hm-sem-state-active, 체크 아이콘
Indeterminate (data-state="indeterminate") Checked와 같은 면, 마이너스 아이콘
Hover (미체크) border --hm-sem-action-primary
Hover (체크·indeterminate) Black 20% overlay (--hm-color-action-overlay-hover)
Focus (:focus-visible) shadcn ring (ring-3 ring-ring/50) 유지
Error (aria-invalid="true") border --hm-sem-action-destructive
Disabled opacity: 0.6 (이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), cursor: not-allowed

5. API

Radix Checkbox props 그대로 (checked / defaultChecked / onCheckedChange, "indeterminate", disabled, required, name, value, form). 추가 prop 없음. 출력 속성: data-slot="checkbox", Radix data-state·data-disabled.

  • Label과의 연결은 <Label htmlFor> 또는 감싸기. Label·Helper·Error 조합은 소비 조합(field.md).

Form 연결 및 Reset

  • Form 내부에서는 기존 native Form 연결을 사용한다. 외부에서는 form="target"과 <form id="target">으로 연결한다.
  • 기본 버튼 렌더링에 form을 지정하면 HM은 Radix의 공개 asChild 경로로 같은 Checkbox 버튼에도 form을 전달한다. 추가 DOM wrapper, 별도 상태, Reset listener 또는 숨김 input 강제 조작은 없다. 호출자가 명시한 asChild 경로는 기존대로 유지한다.
  • Uncontrolled는 defaultChecked로 초기 상태를 지정한다. Native Reset 버튼과 form.reset()은 Radix가 보관한 최초 상태를 복원한다. 이후 defaultChecked 변경을 현재 선택값 변경으로 사용하지 않는다.
  • Controlled는 부모의 checked가 상태의 기준이다. 부모 onReset에서 초기값을 복원하고 onCheckedChange로 변경 요청을 반영한다. 외부 Form에도 내부 Form과 같은 Radix Reset callback 계약이 적용된다.
  • Checked는 name / value로 제출된다(기본 value="on"). Unchecked와 indeterminate는 제출되지 않는다. Indeterminate는 aria-checked="mixed"이며 native input의 checked는 false다. Disabled는 버튼과 숨김 input 모두 비활성화되어 제출되지 않는다.
const initialChecked = true
const [checked, setChecked] = React.useState(initialChecked)

return (
  <>
    <form id="target" onReset={() => setChecked(initialChecked)}>
      <button type="reset">초기화</button>
    </form>
    <Checkbox
      form="target"
      name="agreement"
      checked={checked}
      onCheckedChange={setChecked}
    />
  </>
)

Uncontrolled 예제는 checked / onCheckedChange 대신 defaultChecked={true}를 사용하며 부모의 Reset 상태 관리는 필요하지 않다.

6. Token

Component Mapping 위치: packages/ui/src/theme/checkbox.css (--hm-checkbox-*).

size, radius, border-width, icon-size, background, border-color, hover-border-color, checked-background, checked-border-color, checked-hover-overlay, indicator-color, error-border-color. Semantic(--hm-sem-*)을 참조하고 Input 등 다른 컴포넌트 Token을 쓰지 않는다. Primitive 신규 추가 없음(--hm-sem-action-destructive는 기존 Primitive를 직접 참조).

7. shadcn / Radix에서 유지한 항목

Radix 구조·ARIA(role=checkbox, aria-checked)·키보드, data-slot, cn(..., className), transition-colors, outline-none + focus ring, disabled 커서, lucide CheckIcon.

8. HM에서 변경한 항목

  • shadcn size-4 rounded-[4px] border shadow-xs, dark: 및 data-[state=checked]:bg-primary 계열 class 제거 → theme CSS.
  • MinusIcon 추가(shadcn 기본은 indeterminate 아이콘이 없다). 두 아이콘은 data-state에 따라 CSS로 전환.
  • Disabled opacity 50 → 60.

9. Storybook

  • 대표 Story All("Checkbox"): unchecked, checked, indeterminate, interactive(controlled), hover, focus, error, disabled, long. Focus는 play로 실제 focus.
  • verification: Unchecked, Checked, Indeterminate, Disabled, Error, Playground.

10. 사용자 확정 결정

  • 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다.

11. 미확정 / 보류

  • Checkbox Group 간격, group Label·Error 슬롯, option label 전경 role: 미결정.
  • 수치(18px, 14px 아이콘 등)는 현재 HM 규격이며 디자인 확정 시 재검토.
  • Checkbox Group, Error 문구 슬롯: 컴포넌트 없음(소비 조합).
  • Hit area 확장(24px 타깃 등) 미적용.
  • Disabled opacity 0.6: 이 컴포넌트 값이며 전역 Disabled 규칙으로 승격하지 않는다.

12. 관련 파일

packages/ui/src/components/ui/checkbox.tsx
packages/ui/src/theme/checkbox.css
packages/ui/src/theme.css            (import)
packages/ui/src/index.ts             (export Checkbox)
src/components/checkbox.stories.tsx

13. 검증 상태

  • npm run typecheck, npm run build: 통과(2026-10-02).
  • Storybook computed style: 18×18, border 1px rgb(154,146,132), radius 4px; Checked/Indeterminate bg·border rgb(18,51,40); Error border rgb(211,47,47); Disabled opacity 0.6; 체크/마이너스 아이콘이 상태에 따라 하나만 표시.
  • 실제 interaction: controlled indeterminate → click → checked → click → unchecked, Label 클릭으로 토글 확인.
  • 2026-10-08, React 19.3 / Radix Checkbox 1.3.11: 프로젝트 외부 격리 환경의 수정된 HM 패키지로 실제 브라우저 검증. 수정 전 외부 Form의 true·false·indeterminate Reset 불일치를 재현했고, 수정 후 모두 PASS.
  • Form 내부·외부에서 Space로 상태 변경 후 Native Reset 버튼 및 form.reset()을 실행: defaultChecked true·false·indeterminate와 Controlled 부모 onReset의 상태·ARIA·숨김 input·FormData 복원 확인. Indeterminate 복원 시 숨김 input은 checked=false, indeterminate=true, 제출값 없음.
  • 기존 Radix 동작도 구분해 확인: 최초 indeterminate 렌더에서는 숨김 input의 indeterminate 속성이 false다(내부·외부 및 수정 전·후 동일). UI/ARIA는 mixed, input의 checked=false, FormData 제외는 정상이며 이번 외부 Form Reset 수정에서 이 초기화 동작은 변경하지 않는다.
  • Native Form HTTP POST 8건: 내부·외부·Controlled의 Checked 값은 choice=accepted, Unchecked·indeterminate·Disabled 값은 제외. required native 유효성 및 ARIA, Disabled Tab 제외, BUTTON ref 유지 확인.
  • 수정 전·후 같은 Theme에서 Checked·Unchecked·Indeterminate·Disabled computed style과 아이콘 일치: 18×18px, border 1px, radius 4px, icon 14px, Disabled opacity 0.6. 키보드 focus-visible 3px ring도 일치. CSS·Theme·대표 Story 변경 없음.
  • UI 전체 타입 검사 및 공식 UI 패키지 빌드를 격리 경로에서 통과. 테스트에 사용한 Checkbox 소스와 실제 수정본의 SHA-256 일치. 공용 dist는 이 검증에서 빌드하지 않음.
  • 미확인: 실제 pointer hover, 사이드바 화면, 390px 폭. Hover 관련 CSS와 class는 변경하지 않음.