Files
HMDesign/docs/components/toggle.md
shkim4 4c29894e56 refactor(storybook): simplify component and preview examples
Drop page / section descriptions, state notes and repeated captions from
the representative Stories of 44 components, the Patterns, Center
Graphic and OrgChart pages. Value readouts, spec lists and captions hide
only on the representative page; hidden verification Stories keep them.
Tabs Default API and the Filter Bar draft / applied counts move to the
hidden DefaultApi and DraftApplied Stories; Center Overview is hidden
with its direct URL kept. Story IDs are unchanged.

Align the component, pattern and OrgChart docs with the pages, point the
quality reference check at the DraftApplied Story, and make the P8
styles check expect no section header on the preview-only Expanded page.
2026-10-23 00:28:05 +09:00

120 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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" | "on"`, `aria-pressed` |
| 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"): Overview(Off/On, default·outline), 단독 · 연결 그룹(단독 Toggle, `spacing=0` ToggleGroup 단일·다중 선택), Sizes, States(Disabled off/on). 2026-10-22 Icon-only·icon + text 예시는 숨김 `IconOnly`로만 확인하고, Controlled(`pressed` 표시)·className 확장 예시는 대표 화면에서 제거했다. `pressed` / `onPressedChange`와 `className` 확장 API(§5)는 그대로다.
- 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으로 공유한다.