Files
HMDesign/docs/components/context-menu.md

6.5 KiB

Context Menu

현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Context Menu 고유의 API·Token·상태·결정만 기록한다. Dropdown Menu와 같은 메뉴 계열이므로 상세 값은 dropdown-menu.md와 대응한다.

1. 상태

항목 내용
구현 상태 구현 완료 (현재 HM 규격)
기준일 2026-10-03
기반 shadcn/ui Context Menu (Radix ContextMenu, radix-ui 1.6.7 / @radix-ui/react-context-menu 2.3.7)
대표 Story Components/Overlay/Context Menu → All (이름 "Context Menu")
보류 아래 §11

2. 적용 근거

  • 참고 근거: ui-viz에는 Context Menu 규격이 없다. 최종 기준은 현재 @hm-design/ui 구현과 Dropdown Menu 문서다.
  • 현재 HM 규격: 면·윤곽·radius·그림자는 Floating Surface Semantic(--hm-sem-floating-*)을 source로 쓰고, item·text 값은 Dropdown Menu와 같은 현재 HM 값이다.
  • 구현 방식: shadcn / Radix Context Menu 구조 유지. 상태는 Radix 속성(data-highlighted, data-disabled, data-state)만 쓴다.

3. Dropdown Menu와 역할 차이

Dropdown Menu Context Menu
여는 방법 명시적 버튼(Trigger) 클릭 / Enter / Space / 방향키 대상 영역 우클릭, context-menu key / Shift+F10, 터치 long press
위치 Trigger 기준 anchored (sideOffset 4) 포인터 위치 기준 (sideOffset 없음)
용도 버튼이 가진 Action 목록 대상 객체에 대한 보조 Action
닫힐 때 focus Trigger로 복귀 Trigger가 포커스 가능한 요소일 때만 복귀(§7)

브라우저 native context menu 대신 HM Context Menu가 열린다(Radix가 contextmenu 이벤트를 막음).

4. 기본 규격 / 상태 규격

dropdown-menu.md §3·§4와 값이 같다(Surface White, 1px pale border, radius 4px, 12px glow 그림자, item 36px·14/20, Highlighted Accent 45%(--hm-sem-surface-highlight), Disabled opacity 50, Destructive --hm-sem-action-destructive, Check 16px / Radio 8px). 값이 같아도 --hm-dropdown-menu-*는 참조하지 않는다.

5. API

Radix Context Menu props 그대로. export: ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuGroup, ContextMenuLabel, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuPortal.

  • ContextMenuItem: shadcn variant="default" | "destructive", inset. Label·CheckboxItem·RadioItem·SubTrigger: inset.
  • 출력 속성: data-slot="context-menu-*", Radix data-state·data-highlighted·data-disabled·data-side.
  • HM 전용 prop 없음.

6. Token

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

  • Surface 5개(background, border-width, border-color, radius, shadow)는 Floating Surface Semantic을 source로 참조한다: --hm-sem-floating-background|border-width|border-color|radius|shadow.
  • 나머지(text, padding, item-*, font-*, highlight-*, destructive-*, label-*, shortcut-*, separator-color, indicator-size, radio-dot-size)는 Dropdown Menu와 같은 현재 HM 값을 자체 정의한다.
  • 다른 컴포넌트의 Component Token을 쓰지 않는다. 신규 Primitive·Semantic 없음. 기존 Floating Semantic을 유지한다.

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

Radix 구조·ARIA(role=menu, menuitem*)·키보드(방향키, Home/End, Enter/Space, 오른쪽 방향키 sub menu, Escape, typeahead)·Portal·collision·data-slot·cn(..., className)·open/close animation class·z-50·lucide 아이콘.

접근성 주의: Radix ContextMenuTrigger는 기본적으로 포커스할 수 없는 span이다. 키보드 사용자가 여는 경로(Shift+F10 / context-menu key)를 제공하려면 소비자가 Trigger 영역에 tabIndex={0}을 주거나 asChild로 포커스 가능한 요소(버튼, 행 등)를 감싸야 한다. 우클릭만으로 접근 가능한 기능을 만들지 않는다. 대표 Story의 영역은 tabIndex={0}을 준다.

8. HM에서 변경한 항목

Dropdown Menu와 동일: shadcn 시각 class(rounded, shadow, p-1, focus:bg-accent 등)를 제거하고 theme CSS + data-highlighted로 대체. Radio는 CSS 크기 token을 쓰는 원(context-menu-radio-dot), Check / Radio indicator span에 data-slot="context-menu-item-indicator" 추가. Context Menu에는 sideOffset이 없다.

9. Storybook

  • 대표 Story All("Context Menu"): 점선 영역을 우클릭하면 전체 메뉴가 열린다(Label, Item + Shortcut, disabled, Separator, Checkbox, Radio Group, Sub menu, destructive).
  • verification: Open(로드 시 합성 contextmenu 이벤트로 열린 상태, fullscreen).

10. 사용자 확정 결정

  • 2026-10-03: Context Menu는 Dropdown Menu와 같은 floating menu 계열이므로 Floating Surface Semantic을 Component Token의 source로 쓴다. Component Token은 유지하고 --hm-dropdown-menu-*는 참조하지 않는다(작업 지시).

11. 미확정 / 보류

  • 수치는 Dropdown Menu와 같은 현재 HM 규격이며 디자인 확정 시 함께 재검토.
  • Home/End·typeahead·long press(터치)는 확인하지 못했다.
  • Shift+F10 / context-menu key는 자동화 입력이 브라우저의 native contextmenu 이벤트를 만들지 못해 확인하지 못했다(Radix는 이 이벤트로 여는 구조이며 우클릭과 같은 경로).
  • 메뉴가 닫힌 뒤 Trigger가 포커스 불가면 focus는 body에 남는다(Radix 동작).

12. 관련 파일

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

13. 검증 상태

  • npm run typecheck, npm run build: 통과(2026-10-03).
  • 실제 interaction(Browser pane): 우클릭 시 native 메뉴 대신 HM 메뉴가 열림, 방향키 이동(disabled 항목 건너뜀), Enter로 Checkbox 토글(메뉴 유지), 방향키 아래 + 오른쪽으로 Sub menu 열림과 첫 항목 focus, Escape 두 번으로 sub → menu 순서 닫힘.
  • computed style: Content·Sub Content 모두 white, 1px solid rgb(222,219,210), radius 4px, 0 0 12px .2 그림자, item 36px, Dropdown Menu(Open Story) 값과 동일. role menu.
  • Dropdown Menu의 같은 값이 변하지 않음을 확인(회귀 없음).