Files
HMDesign/docs/components/toggle.md
T

8.1 KiB
Raw Blame History

Toggle

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

1. 상태

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

2. 적용 근거

  • 참고 근거: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Toggle이 없다(인벤토리에 Switch 계열 기능 이름으로만 언급). 참고할 수치가 없었다.
  • 현재 HM 규격: 아래 §3~§4. 현재 HM Semantic/Primitive로 새로 정했다.
  • 구현 방식: shadcn / Radix Toggle 구조와 variant(default | outline), size(default | sm | lg) API를 유지하고, 모양은 HM 독립 Token(--hm-toggle-*)으로 정한다.
  • Button과의 관계: 이 문서 §14.
  • 보류: §11.

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

항목 값
Size sm 28px, default 34px, lg 42px (높이). padding-x 8 / 12 / 16px
Icon-only 단일 React element + 직계 svg 하나인 경우 data-icon-only와 CSS로 padding 0. SVG + text node는 size별 padding 8/12/16px 유지
Radius --hm-radius-control-md (4px)
Typography 14 / 20 / 500, icon + text는 gap 8px
Off 배경 투명, 글자 --hm-sem-fg-primary
On 배경 --accent, 글자 --accent-foreground (full Accent; Select 선택 항목의 Accent 45% 면과는 별도 정책)
outline variant 1px --hm-sem-stroke-input-default border(default는 border 없음)
커서 pointer

4. 상태 규격

상태 값
Off / On Radix `data-state="off"
Hover (off) 배경 --accent 45% (on 상태는 그대로 On 면)
Selected + Hover (on) 배경 --accent 100%, 글자·아이콘 --accent-foreground 유지. Hover 45% 면으로 내려가지 않음
Focus (:focus-visible) shadcn ring (ring-3 ring-ring/50) 유지
Disabled opacity: 0.6(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), cursor: not-allowed, 클릭·hover 무반응

5. API

Radix Toggle props 그대로. export: Toggle, toggleVariants.

  • pressed/defaultPressed/onPressedChange, disabled, asChild.
  • variant "default" | "outline", size "default" | "sm" | "lg"(shadcn 호환). 출력 data-variant, data-size, data-slot="toggle".
  • icon-only Toggle은 소비자가 aria-label을 준다(Story 참고).
  • 새 variant는 만들지 않았다(default, outline 두 개만 유지).

6. Token

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

height-sm|md|lg, padding-x-sm|md|lg, radius, gap, font-size|line-height|font-weight, off-background|off-text, hover-background, on-background|on-text, outline-border-width|color, disabled-opacity. Semantic과 Theme palette(--hm-sem-stroke-input-default)만 참조한다. --hm-button-*와 Button의 role 체계는 참조하지 않는다. 신규 Primitive와 Semantic 없음.

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

Radix 구조·aria-pressed·data-state·Enter/Space·controlled/uncontrolled·disabled·asChild, shadcn variant/size API와 toggleVariants(cva), focus ring class, svg 정렬 class, data-slot, cn(..., className).

8. HM에서 변경한 항목

  • shadcn toggleVariants의 색·크기 class(h-8 min-w-8 px-2.5, hover:bg-muted, data-[state=on]:bg-muted, outline border-input shadow-xs)를 제거하고 theme CSS로 이전. On 면은 muted가 아닌 accent.
  • 기본 모양을 @layer components에 두어 소비자의 className(폭, radius, 레이아웃 utility)이 이긴다. 다른 theme CSS(레이어 밖)와 달리 Toggle은 Story에서 className="w-40 rounded-full"이 적용되는 것을 확인했다.

9. Storybook

  • 대표 Story All("Toggle"): Off/On(default·outline), Icon-only와 icon + text(BoldIcon, ItalicIcon, StarIcon), Sizes, Disabled(off/on), Controlled(pressed 상태 표시), className 확장을 한 페이지에 둔다.
  • verification: States, Icon Only, Disabled.

10. 사용자 확정 결정

  • 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). Toggle은 Button과 독립 Component Token을 쓴다(작업 지시).
  • 2026-10-06 Selected 원복: Git baseline d101530의 --accent / --accent-foreground 매핑을 복원했다. 아이콘은 currentColor로 전경을 따른다. Border는 baseline대로 default 투명 / outline --hm-sem-stroke-input-default이며 Selected 전용 border는 없다. Active Semantic은 참조하지 않고 Focus는 기존 --ring → --hm-sem-stroke-input-focus(Light 2)와 ring-ring/50을 유지한다. Hover·Disabled·Icon-only / Icon+Text padding·Theme JSON·Runtime 계약 변경 없음.

11. 미확정 / 보류

  • 수치(28/34/42px, padding, On 면 accent, Disabled opacity 0.6)는 현재 HM 규격이며 디자인 확정 시 재검토.
  • On 상태 hover는 On 면 그대로이다(별도 hover 면을 정하지 않음).
  • On 상태를 Primary(진한 녹색 면)로 강하게 표시하는 variant는 만들지 않았다.
  • 아이콘 크기는 shadcn 기본(16px) 유지.

12. 관련 파일

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

13. 검증 상태

  • 2026-10-06 Selected 원복 검증: Storybook All / ContentNodes에서 Default·실제 Hover·Selected·Selected + Hover·키보드 Focus·Disabled·Icon-only·Icon+Text 확인. Selected 배경 rgb(189, 223, 212) / 전경·아이콘 rgb(18, 51, 40), Focus는 Light 2 #266651 50%의 3px ring. Runtime API의 Active override는 Selected에 영향 없음, Accent/on Accent override는 면·전경만 변경, Focus override는 ring만 변경. 1280/390px에서 sm/default/lg Icon-only 28/34/42px square·padding 0, Icon+Text/Text-only padding-x 8/12/16px 및 Array/Fragment 조합 유지. typecheck, build, build-storybook, git diff --check 통과(기존 CSS 예시·Storybook chunk 경고 유지).

  • 2026-10-06 Visual QA: 1280/1024/768/390px, sm/default/lg에서 Icon-only PL/PR 0/0px·28/34/42px square, Icon+Text와 Text-only PL/PR 각각 8/12/16px, pressed 상태 정상. Array/Fragment icon과 Fragment/element label 조합도 확인. typecheck, build, build-storybook 통과.

  • npm run typecheck, npm run build: 통과(2026-10-02).

  • 실제 interaction(Browser pane): 클릭으로 off → on → off(aria-pressed, data-state 일치), 키보드 focus 후 Enter로 on, Space로 off, disabled는 클릭과 focus 불가, controlled는 클릭으로 pressed = true와 data-state="on" 동기화.

  • computed style: default 34px / On 배경 rgb(189,223,212)·글자 rgb(18,51,40), outline border rgb(222,219,210), sm 28px, lg 42px, icon-only 34×34, disabled opacity 0.6, className="w-40 rounded-full" 적용(폭 160px, radius 최대).

  • 미확인: 실제 pointer hover 면 시각, focus ring 시각, 390px(단일 버튼이라 폭 문제 없음).

14. 역할 구분 (Button · Checkbox · Radio Group · Switch와의 차이)

컴포넌트 역할
Button Action 실행(일회성). variant가 역할(primary/secondary/destructive…), appearance가 treatment
Toggle pressed 상태 유지(툴바식 on/off). aria-pressed
Toggle Group 선택 그룹(단일/다중 선택, roving focus)
Checkbox 폼의 boolean / 다중 선택
Radio Group 폼의 배타적 선택
Switch 설정 on/off(즉시 적용되는 설정 값)

Toggle은 Button과 모양이 비슷해 보여도 Button의 action 중심 variant 체계(role/appearance)와 --hm-button-* Token을 상속하지 않는다. 역할이 다르고, Button 규격이 바뀔 때 Toggle이 함께 바뀌면 안 되기 때문이다. 공통 의미(선택 면의 accent, control radius)만 Semantic으로 공유한다.