8.8 KiB
Navigation Menu
현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Navigation Menu 고유의 API·Token·상태·결정만 기록한다. Dropdown Menu(dropdown-menu.md), Menubar(menubar.md)와 값을 공유하지 않는다.
1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | 구현 완료 (현재 HM 규격) |
| 기준일 | 2026-10-02 |
| 기반 | shadcn/ui Navigation Menu (Radix NavigationMenu) |
| 대표 Story | Components/Navigation/Navigation Menu → All (이름 "Navigation Menu") |
| 보류 | 아래 §11 |
2. 적용 근거
- 참고 근거: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Navigation Menu 규격이 없다. 현재 위치(Current) 의미와 Hover/Focus는 독립이라는 ui-viz의 navigation 안내만 참고했다.
- 현재 HM 규격: 아래 §3~§4. 현재 HM Semantic 체계로 새로 정했다. Dropdown Menu의 운영 원칙(Radix 상태 속성, Escape, focus 복귀, Portal/viewport)만 참고하고 token은 참조하지 않았다.
- 구현 방식: shadcn / Radix NavigationMenu 구조(Root, List, Item, Trigger, Content, Link, Viewport, Indicator) 유지, theme CSS로 HM 스타일 연결.
- 역할 구분: 목적지 이동(링크) 중심이다. Dropdown Menu(명령)·Menubar(메뉴 바)와 접근성 모델이 다르다(
role=menu가 아니라 disclosure + link). - 보류: §11.
3. 기본 규격 (현재 HM 규격)
| 항목 | 값 |
|---|---|
| 상위 Trigger / Link | 높이 34px, padding 0 12px, radius 4px, 14 / 20 / 500, --hm-sem-fg-primary, 항목 gap 4px |
| Trigger indicator | shadcn ChevronDownIcon 12px, open 시 180° 회전 |
| Content / Viewport 면 | --hm-sem-bg-surface, 1px --hm-sem-floating-border-color, radius 4px, shadow 0 0 12px rgb(0 0 0 / .2) |
| Content | padding 8px, min-width 240px, 폭은 안의 내용이 정함 |
| Content 안 Link(목적지) | 세로 배치, padding 8px 12px, gap 2px, radius 2px. data-description 자식은 12 / 18 / 400 secondary |
| Indicator(화살표) | shadcn 회전 사각형, --hm-sem-floating-border-color |
| 열기 | hover(Radix 기본 delay)·click·keyboard. 이동 animation은 shadcn 기본 |
4. 상태 규격
| 상태 | 값 |
|---|---|
| Hover / Focus / Open (Trigger) | 배경 --accent, 글자 --accent-foreground. Focus는 ring 모양 0 0 0 3px ring 50% 추가 |
| Link Hover / Focus | Trigger와 같은 면 |
Active Link (active) |
Radix data-active + aria-current="page". 글자 --hm-sem-action-primary, weight 700, 배경 --accent 45% (hover면 일반 hover 면) |
| Disabled Trigger | opacity: 0.6(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), cursor: not-allowed, native disabled |
| Open / Closed | Radix data-state, 이동 방향은 data-motion (shadcn animation) |
5. API
Radix NavigationMenu props 그대로. export: NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport.
NavigationMenu:viewport(기본 true, shadcn).false면 각 Content가 자기 Trigger 아래에 열린다. 출력data-viewport.NavigationMenuLink:active(현재 위치),asChild로 router Link 조합(router에 의존하지 않음). disabled link는 지원하지 않는다.NavigationMenuTrigger: chevron 내장.disabled는 native 속성.NavigationMenuIndicator: 선택 사용(Story의 viewport 예시에서 사용).- 2026-10-06 Visual QA: viewport 모드의 Item은 static으로 두어 Radix List wrapper가 Trigger의
offsetParent가 되도록 한다.viewport={false}에서만 Item의 relative anchor를 유지한다. Indicator에 임의 위치 보정값을 넣지 않는다.IndicatorAlignmentverification Story는 서로 다른 label width의 첫·중간·마지막 Trigger와 두 viewport 모드를 검증한다. - 대표 Story의 고정 360/300px 내용은 좁은 화면에서 자연 폭을 사용한다. Desktop 폭도 Story frame container에 제한하며, 모바일 inline Content의 absolute 배치는 Story 범위에서만 적용한다(Production CSS 변경 없음).
- 새 prop은 만들지 않았다. Content 안 title/description은 소비 조합이며
data-description속성으로 보조 문구를 표시한다.
6. Token
Component Mapping 위치: packages/ui/src/theme/navigation-menu.css (--hm-navigation-menu-*).
height, padding-x, gap, radius, font-size|line-height|font-weight, text, icon-size, hover-background|text, active-background|text|font-weight, background, border-width|color, surface-radius, shadow, content-padding, content-min-width, item-padding|gap|radius, description-*, disabled-opacity. Semantic과 Theme palette(--hm-sem-floating-border-color)만 참조한다. --hm-dropdown-menu-*, --hm-tab-*, --hm-button-*는 참조하지 않는다. 신규 Primitive 없음.
7. shadcn / Radix에서 유지한 항목
Radix 구조·동작(hover open delay, Arrow 키와 Tab 이동, Escape 닫힘, focus 복귀, Viewport 크기 측정, Indicator 위치), ARIA(aria-expanded, aria-controls, aria-current), 이동 animation(data-motion), viewport/non-viewport 구조, chevron, data-slot, cn(..., className).
8. HM에서 변경한 항목
- shadcn
navigationMenuTriggerStyle(rounded-md, h-9, bg-background, hover/focus/openbg-accent/50)과 Content·Viewport의bg-popover text-popover-foreground rounded-md border shadow등을 제거하고 theme CSS로 이전. - Content 안 Link는 shadcn에서 별도 class로 꾸미던 것을 선택자(
content하위link)로 처리. - Active Link 스타일(색·weight·배경)을 정의(shadcn은
data-[active=true]:bg-accent/50).
9. Storybook
- 대표 Story
All("Navigation Menu"):Viewport(기본)과viewport={false}두 구성을 한 페이지에 둔다. 제품(설명 있는 3 링크), 자료(2열 4 링크), 회사(현재 위치 링크), 문의(일반 링크), 준비 중(disabled Trigger). - verification:
Default,No Viewport. - Story의 Content 폭은
style인라인으로 지정한다(새 Story 파일의 Tailwind 임의값 클래스가 개발 서버에서 생성되지 않는 일이 있었다).
10. 사용자 확정 결정
- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다.
11. 미확정 / 보류
- 수치(34px, 12px padding, Content 240px 최소 폭, 그림자, Active 표현)는 현재 HM 규격이며 디자인 확정 시 재검토.
- 모바일: Radix NavigationMenu는 데스크톱 GNB 구조다. 390px에서 Viewport의 폭은 Content 폭(Story에서 360px)을 따라가며
max-width제한이 없어 우측 끝에 닿았다(가로 overflow는 없음). 모바일 navigation(햄버거, Sheet/Drawer 조합 등)은 GNB/Layout 단계에서 별도 패턴으로 검토한다. - disabled link 지원 없음. 필요하면
aria-disabled규칙을 Pagination과 같은 방식으로 후속 검토. - Indicator 사용 여부와 색은 현재 선택 사항이며 별도 규격이 없다.
- 열림/닫힘 Motion은 shadcn 기본.
12. 관련 파일
packages/ui/src/components/ui/navigation-menu.tsx
packages/ui/src/theme/navigation-menu.css
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export)
src/components/navigation-menu.stories.tsx
13. 검증 상태
-
2026-10-06 Visual QA: 1280/1024/768/390px에서 제품 Trigger/Arrow center 48.8828/49px, 자료 118.6484/119px. 다른 label width(제품/기술 자료/지원 센터)도 Arrow 오차 절댓값 0.45px 이내. inline 모드의 각 Content가 자기 Trigger 아래에 열림 확인. 대표 Story 두 모드의 열린 패널이 viewport 안에 있고 페이지 가로 overflow 없음.
typecheck,build,build-storybook통과. -
npm run typecheck,npm run build: 통과(2026-10-02). -
실제 interaction(Browser pane): 키보드로 Enter 열림(
aria-expanded=true), Tab으로 Content 안 첫 링크, 다음 링크로 이동, Escape로 닫히고 Trigger로 focus 복귀, ArrowRight로 다음 상위 항목으로 이동, 마우스 hover로 해당 Content가 열림, 현재 위치 링크가aria-current="page"·data-active·weight 700. -
computed style: Viewport 흰 면, border
rgb(222,219,210), radius 4px, shadow0 0 12px .2. Content 폭이 안의 목록(360px) 폭에 맞춰 376px. -
390×844: 열린 Viewport 폭 378px, 좌측 16px에서 시작해 오른쪽 끝에 닿음(가로 overflow 없음). 한계는 §11.
-
미확인: 닫힘 animation 시각,
viewport={false}구성의 열림(코드 경로는 Radix/shadcn 기본), 두 Content 사이 이동 animation 시각, ArrowDown으로 Content 진입.