8.1 KiB
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, outlineborder-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#26665150%의 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 borderrgb(222,219,210),sm28px,lg42px, 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으로 공유한다.