Files
HMDesign/docs/components/command.md
T
2026-10-28 14:03:46 +09:00

7.3 KiB

Command

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

1. 상태와 성격

항목 내용
성격 Primitive component (검색 가능한 command/list). Combobox 자체가 아니다
구현 상태 구현 완료 (현재 HM 규격)
기준일 2026-10-02
기반 shadcn/ui Command = cmdk
대표 Story Components/Search & Date/Command → All (이름 "Command")
보류 아래 §11

2. 적용 근거

  • 참고 근거: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Command가 없다(Search Field의 후보 목록은 다른 컴포넌트). 참고할 수치가 없었다.
  • 현재 HM 규격: 아래 §3~§4. 현재 HM Semantic/Primitive로 새로 정했다. Input·Popover·Dropdown Menu Token은 참조하지 않는다(Command Input은 Command 전용 Token).
  • 구현 방식: cmdk가 검색 필터링, 방향키/Home/End 이동, Enter 선택, disabled 건너뜀, 그룹, Empty, listbox/option ARIA를 소유한다. HM은 cmdk의 속성(data-selected, data-disabled, [cmdk-group-heading])에 시각을 연결한다.
  • 범위: 전역 Command Palette(CommandDialog)는 만들지 않았다(별도 고급 패턴).
  • 보류: §11.

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

항목 값
Command 흰 면(--hm-sem-bg-surface), 글자 --hm-sem-fg-primary, radius 4px. border·shadow 없음(Popover 등 컨테이너 안에서 쓰는 것을 전제)
Input 높이 40px, padding-x 12px, 14 / 20, 아래 1px --hm-sem-stroke-input-default 구분선, 검색 아이콘 16px --hm-sem-fg-secondary, placeholder --hm-sem-fg-placeholder
List 최대 높이 280px(넘치면 세로 scroll), padding 4px
Item 최소 높이 36px, padding-x 12px, gap 8px, radius 2px, 14 / 20, 아이콘 16px
Group heading 12 / 18 / 500, --hm-sem-fg-secondary, padding 6px 12px
Separator 1px --hm-sem-stroke-input-default, List 폭 전체
Shortcut 12px, --hm-sem-fg-secondary, letter-spacing .05em
Empty padding 24px 12px, 가운데, --hm-sem-fg-secondary

4. 상태 규격

상태 값
Highlighted (data-selected="true") 배경 Accent 45%(--hm-sem-surface-highlight), 글자 --accent-foreground. 포인터 hover와 키보드 이동이 공유
Disabled (data-disabled="true") opacity 0.6(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), cursor not-allowed, 방향키 이동에서 제외
Empty 필터 결과가 없을 때 CommandEmpty 표시
Focus Input에 있음(outline-hidden). 이동은 Highlighted 면으로 표시

5. API

cmdk props 그대로. export: Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut.

  • Command: value/onValueChange(highlight), filter(사용자 정의 필터), shouldFilter, loop, label.
  • CommandItem: value, keywords, disabled, onSelect.
  • CommandInput: Input + 검색 아이콘 wrapper(data-slot="command-input-wrapper")를 포함한다.
  • 필터링은 cmdk 기본(fuzzy) 그대로이며 HM이 검색 알고리즘을 만들지 않았다.
  • 접근성: CommandInput은 role="combobox", aria-expanded, aria-controls(listbox), aria-autocomplete="list"; CommandList는 role="listbox"; CommandItem은 role="option".

6. Dependency

  • cmdk ^1.1.1 (packages/ui에 추가, shadcn Command의 기반). CENTER 패키지에는 추가하지 않았다.

7. Token

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

background, text, radius, input-*(height, padding-x, gap, font, placeholder, icon), divider-color, list-max-height|padding, item-*(height, padding-x, gap, radius, font), highlight-background|text, group-label-*, shortcut-*, empty-*, disabled-opacity. Semantic과 Primitive(--hm-warm-neutral-pale)만 참조한다. 신규 Primitive와 Semantic 없음. 이 CSS는 cmdk 내부 속성에 맞춘 일반 CSS이며 @layer를 쓰지 않는다(className으로 Command의 높이 등을 바꾸는 것은 TSX class 영역).

8. cmdk에서 유지한 항목

필터링과 정렬, 키보드(ArrowUp/Down, Home/End, Enter, disabled 건너뜀), 그룹 숨김(결과 없는 그룹), Empty, data-selected/data-disabled, listbox/option ARIA, 마우스 hover가 highlight를 옮기는 동작.

9. Storybook

  • 대표 Story All("Command"): Search + disabled item(선택 로그는 숨김 Search에만 표시), Groups·icons·shortcut(CalendarIcon, UserIcon, SettingsIcon), Long list(30개, List가 scroll)를 한 페이지에 둔다. 테두리 박스는 Story 조합이다.
  • verification: Search, Grouped.
  • 2026-10-28: Search 예제는 Next.js(next), SvelteKit(sveltekit), Remix(remix, 비활성), Astro(astro) 순서로 표시한다. Combobox 예제와 표시명·내부 값을 맞추며, 표시명 검색도 유지한다. §13의 과일 항목 검증은 변경 전 기록으로 보존한다.

10. 사용자 확정 결정

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

11. 미확정 / 보류

  • 수치(40px Input, 36px Item, 280px List 최대 높이)는 현재 HM 규격이며 디자인 확정 시 재검토.
  • Command Palette(CommandDialog, 전역 단축키 호출)는 별도 패턴으로 남겼다.
  • 로딩 상태(Command.Loading), 중첩 페이지는 스타일링하지 않았다.
  • 선택됨(value) 표시는 Command에 없다(highlight와 다르다). Combobox가 check 아이콘으로 따로 표시한다.
  • Command의 Escape는 Command 자체에서는 하는 일이 없다(Popover/Dialog 안에서는 컨테이너가 닫는다).

12. 관련 파일

packages/ui/src/components/ui/command.tsx
packages/ui/src/theme/command.css
packages/ui/package.json             (dependency: cmdk)
packages/ui/src/theme.css            (import)
packages/ui/src/index.ts             (export)
src/components/command.stories.tsx

13. 검증 상태

  • npm run typecheck, npm run build: 통과(2026-10-02).
  • ARIA(Browser pane): Input role=combobox·aria-expanded=true·aria-controls·aria-autocomplete=list, List role=listbox, Item role=option, disabled Item에 data-disabled="true".
  • keyboard: 첫 항목이 처음부터 highlight, ArrowDown으로 이동(비활성 항목 건너뜀: 바나나 → 포도), Enter로 선택(onSelect 로그 갱신), 그룹 간 이동(ArrowUp 환경설정 → 프로필), End로 마지막 활성 항목(비활성 결제는 제외).
  • 검색: 한글 "바나" 입력 시 바나나만 표시, 결과 없는 "zzz"는 Empty 문구, 입력을 지우면 전체 4개로 복귀하고 첫 항목이 highlight. Escape는 Input focus를 유지(Command 단독에서는 동작 없음).
  • computed style: Input 래퍼 40px, List 최대 280px·padding 4px, Item 36px·padding 0 12px·14/20, Highlighted rgb(189,223,212), Group heading 12px/500 rgb(110,103,89), Shortcut 12px.
  • 미확인: 긴 List의 키보드 scroll 시각, 모바일 폭(Combobox 안에서만 확인), pointer hover 시각, CommandItem의 keywords 외 사용자 정의 filter.