Files
HMDesign/docs/components/select.md
T

11 KiB
Raw Blame History

Select

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

1. 상태

항목 내용
구현 상태 HM 스타일 적용 완료 (hm-select-trigger class 없음)
기준일 2026-10-02
기반 shadcn/ui Select, Radix Select
대표 Story Components/Form/Select → All (이름 "Select")
보류 Item Disabled, 일반 Focus 두께 등 (§11)

2. 적용 근거

  • ui_viz Confirmed: size/높이(select.md §1.1 D-004, §8), 색·border(§10, §1.3, D-005, M06), indicator glyph(§7, 사용자 제공 Source), Trigger/Content 연결 윤곽(§8).
  • ui_viz BASELINE_VALUE: padding-x 12, gap 12, option 높이 42(§8, §17) — "현재 적용 계약"으로 표기된 값.
  • HM 사용자 결정: Trigger Disabled opacity 0.6(2026-10-02, §10).
  • shadcn/Radix 유지: 구조·접근성·positioning(§7).
  • HM 사용자 결정(2026-10-06): Error는 Destructive border, Keyboard Focus는 독립 Focus Semantic ring.

3. 기본 규격

Size 외곽 Trigger 내부 높이 Trigger 글자
medium 34 32 14 / 20 / 400
large (기본) 44 42 16 / 24 / 400
  • 높이는 min-height = 내부 높이 + 1px border × 2.
  • Padding-x 12, gap 12, border 1px --hm-sem-stroke-input-default, radius --hm-radius-input, face --hm-sem-bg-surface.
  • Placeholder([data-placeholder]): --hm-sem-fg-placeholder.
  • Trigger와 Value는 min-width: 0; 긴 placeholder/선택값은 한 줄 ellipsis로 줄고 18px indicator를 밀지 않는다. ui_viz 공개 Story에 없던 현재 HM 복원력 규칙이며 parity 판정 근거로 쓰지 않는다.
  • Icon: HM glyph, 18×18 viewBox, M5 7 L9 11 L13 7, stroke 1.50542, 색 #2d2d2d(--hm-select-indicator-color).
  • Content: Trigger와 같은 폭(--radix-select-trigger-width), border 1px focus 색, bottom일 때 아래쪽만 radius.
  • Option: Trigger의 size를 따른다. min-height 42(두 size 공통, ui_viz §8), padding-x 12, 글자 Medium 14/20/400(Medium Trigger와 동일) · Large 16/24/400, radius 0. ui_viz는 Medium Option 글자 Role을 TBD로 남겼고, 2026-10-06 Trigger 값에 맞추기로 했다(사용자 요청). 높이·폭은 size로 바꾸지 않는다(Content = Trigger 외곽 폭).
  • size 전달: SelectTrigger size가 유일한 지정처다. Select가 내부 context로 받아 Portal된 SelectContent의 data-size로 노출하고, select.css가 [data-slot="select-content"][data-size]로 Option profile을 정한다. SelectContent·SelectItem에는 size prop이 없다.

4. 상태 규격

상태 값 근거
Default 위 기본 규격 ui_viz Confirmed
Focus (:focus) border 색 → --hm-sem-stroke-input-focus, 두께 1px 유지 ui_viz Confirmed (1.5px 두께는 ui_viz FOLLOW_UP으로 보류)
Open ([data-state="open"]) border 색 → focus 색, 위쪽 모서리만 round(리스트와 이어짐) ui_viz Confirmed
Selected (Item [data-state="checked"]) Hover와 같은 배경 면(--hm-sem-surface-highlight, Accent 45%), border·check icon 없음 표현: ui_viz Confirmed / 색: shadcn Token(accent)
Highlighted (Item focus:/data-highlighted) Accent 45%(--hm-sem-surface-highlight) 위와 동일
Disabled (Trigger :disabled) Default paint 유지, opacity: 0.6, cursor: not-allowed opacity: 사용자 결정
Disabled (Item data-disabled) shadcn 기본 opacity-50, 포인터 차단 미검증(§11)
Invalid (aria-invalid="true") border 1px --hm-sem-action-destructive, Error ring 없음 2026-10-06 사용자 결정
Invalid + Keyboard Focus (:focus-visible) Destructive border 유지 + Focus Semantic 3px / 50% outer ring 위와 동일
Invalid + Open Trigger Destructive border, 연결 seam은 Neutral. Content의 기존 Surface / Focus outer border / Accent 45% Option 유지 위와 동일
Disabled Invalid Destructive border + 기존 opacity 0.6, focus 불가 위와 동일

5. API

SelectTrigger size:
  "sm" | "default"
  →
  "medium" | "large"      기본 "large"

SelectContent sideOffset:
  shadcn 기본 4
  →
  기본 -1  (Trigger와 1px border를 공유)

SelectItem:
  children 앞의 ItemIndicator(check icon) 제거. children만 ItemText로 렌더.
  • Select, SelectValue, SelectGroup, SelectLabel, SelectSeparator, 스크롤 버튼은 shadcn API 그대로.
  • 출력 속성: data-slot="select-trigger", data-size. HM 전용 error/open/focused prop은 없다. 상태는 Radix 속성(data-state, data-placeholder, data-highlighted, data-side)과 :disabled로 적용한다.

6. Token

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

  • 재사용한 공통 Semantic: --hm-radius-input(radius), --hm-sem-bg-surface, --hm-sem-fg-primary, --hm-sem-fg-placeholder, --hm-sem-stroke-input-default, --hm-sem-stroke-input-focus.
  • Select 전용 값: --hm-select-control-height-{md,lg}(32/42), --hm-select-padding-x, --hm-select-gap, --hm-select-option-height, --hm-select-font-size-*, --hm-select-disabled-opacity, --hm-select-indicator-color.
  • indicator는 --hm-select-indicator-color → --hm-sem-palette-indicator를 참조한다(Default #2d2d2d). 신규 Primitive 없음.
  • Input·Button과 값(42 등)이 겹쳐도 alias하지 않는다. 각 ui_viz 규격이 따로 확정한 값이다.
  • --hm-select-disabled-opacity: 0.6은 Select 전용 Component Token이며 Input의 Disabled와 공유하지 않는다.

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

Radix Root/Trigger/Portal/Content/Viewport/Item/ItemText 구조, 키보드 조작·typeahead·focus 관리, Portal, popper positioning(position="popper", align="start"), 스크롤 버튼, 열림/닫힘 animation class, data-slot, cn(..., className), Trigger의 native aria-invalid 전달, Item의 data-disabled 처리.

8. HM에서 변경한 항목

  • Size API(sm|default → medium|large), 기본 large, data-size.
  • sideOffset 기본 -1, Content 폭을 Trigger 폭에 맞춤, Trigger/Content 연결형 윤곽.
  • chevron 아이콘을 HM glyph(inline SVG)로 교체.
  • Item check indicator 제거, Selected를 배경 면으로 표현.
  • shadcn Trigger의 높이·padding·border·focus ring class 제거 → theme CSS가 담당.
  • hm-select-trigger class 제거.
  • 변경하지 않음: 스크롤 버튼의 lucide chevron(ChevronUp/Down).

9. Storybook

  • 대표 Story All("Select"): 구역 default(Large, size 생략), selected, disabled, open.
  • verification(tags: ["verification"]): Default, Open, Selected, Disabled, Sizes("Compare Sizes"), Medium, Large, LongContent, Scroll, Playground(HM 전용).
  • Disabled Story 구성: Trigger 비활성 상태들(placeholder 포함).
  • InvalidFocus verification Story: 앞/뒤 버튼 사이의 Invalid Trigger와 Disabled Invalid Trigger. Tab / Shift+Tab, Enter / Space, Escape 및 focus return을 실제 키보드로 확인한다.
  • LongContent와 Scroll은 ui_viz 공개 Story parity가 아니라 좁은 폭·긴 값·Radix scroll 회귀를 확인하는 HM 전용 Story다.
  • sidebar는 All 하나만 보이는 1뎁스. 운영 규칙은 지침 §6.3, §7 참조.

10. 사용자 확정 결정

2026-10-02

  • Select Trigger Disabled opacity 0.6 확정(select.css의 --hm-select-disabled-opacity).
  • 같은 날 Input Disabled 0.6도 확정되었다(input.md 참조). 현재 적용 범위는 Input / Select이며 전체 디자인 시스템 Disabled opacity로 자동 확대하지 않는다.
  • Select Item의 Disabled opacity는 이 결정에 포함하지 않는다. Trigger와 같은 규격이라고 판단하지 않는다.

11. 미확정 / 보류

  • Item Disabled: 현재 shadcn 기본 opacity-50. 사용자 결정·ui_viz 근거 없음. 미검증.
  • FOLLOW_UP(ui_viz): Focus/Open 상태의 border 1.5px 두께.
  • ui_viz 미정의: Group/Label/Separator 규격, 스크롤 버튼 규격, item-aligned position. Error 표현은 2026-10-06 HM 사용자 결정이 우선한다.
  • BASELINE_VALUE: padding-x 12, gap 12, option 42는 "현재 적용 계약"이며 별도 Confirmed 결정은 아니다.
  • Selected/Highlighted는 현재 공통 Accent 45% 정책을 쓴다. ui_viz Temporary 표기는 도입 당시 근거다.

12. 관련 파일

packages/ui/src/components/ui/select.tsx
packages/ui/src/theme/select.css
src/components/select.stories.tsx
src/components/story-board.tsx

13. 검증 상태

  • 2026-10-05: npm run typecheck, npm run build, npm run build-storybook 통과. 기존 generated CSS의 bg-[var(...)] 경고와 500kB chunk warning은 유지된다.
  • Storybook computed style: Medium/Large Trigger 34/44px, padding-x 12px, radius 4px, border 1px, Option 42px·16/24/400, Disabled Trigger opacity 0.6 확인.
  • Open: Trigger와 Content 폭 동일, sideOffset=-1 접점, Focus/Open #123328, Content border/radius/shadow와 Portal listbox 확인.
  • Interaction: click open, ArrowUp/Down, Home/End, typeahead, Enter select, Escape, focus return, Disabled open 차단, 32개 Option의 wheel/keyboard scroll 확인.
  • Responsive: 1280/768/390px에서 popup clipping·문서 가로 overflow 없음. 390px의 280px Trigger에서 긴 placeholder/선택값 ellipsis와 indicator 비중첩 확인.
  • Story: All의 공개 구역 4개와 숨김 LongContent/Scroll verification Story 확인.
  • 미확인/보류: Item Disabled 시각, collision으로 side="top"이 된 연결 윤곽의 별도 디자인(원 ui_viz도 viewport edge flip TBD), 브라우저별 렌더 차이.

Invalid + Focus 검증 (2026-10-07)

  • Focus ring: --ring → --hm-sem-stroke-input-focus → --hm-color-border-focus. Invalid border: 기존 --hm-sem-action-destructive. 새 Runtime Role / Production Error prop / 메시지 컴포넌트 없음.
  • Chromium computed style: Invalid border rgb(211, 47, 47), Invalid + Keyboard Focus box-shadow color(srgb 0.14902 0.4 0.317647 / 0.5) 0px 0px 0px 3px, outline-style: none. Invalid 단독은 box-shadow: none.
  • Theme Designer UI에서 Focus #c75b00, Destructive #d41466, Active #007f73, Accent #ffd36d로 분리. Invalid border rgb(212, 20, 102) + Focus shadow color(srgb 0.780392 0.356863 0 / 0.5) 0px 0px 0px 3px 확인.
  • npm run typecheck, npm run build, npm run build-storybook 통과. 기존 문서 예시 CSS warning 및 Storybook chunk 크기 / plugin timing warning 유지.
  • 검증 범위: Chromium 환경. 다른 브라우저의 렌더링은 별도 확인이 필요하다.
  • 숨김 InvalidFocus에서 Closed Invalid / Tab Focus / Invalid Open / 방향키 강조 / Disabled Invalid 확인. Invalid border computed 1px; Tab 전후 shadow none → 3px Focus ring. Tab은 Disabled를 건너뛰고, Shift+Tab으로 복귀. Enter / Space open, ArrowDown 이동, Escape close 및 Trigger focus return 확인.
  • Invalid + Open은 Trigger Destructive border와 Neutral seam rgb(222, 219, 210)을 동시에 유지. Popup white Surface, 외곽 rgb(199, 91, 0)(Focus), Selected / Highlighted color(srgb 1 0.827451 0.427451 / 0.45)(Accent) 확인. Popup에 Error 색 없음. 대표 All 회귀 확인.