# 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에 임의 위치 보정값을 넣지 않는다. `IndicatorAlignment` verification 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/open `bg-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, shadow `0 0 12px .2`. Content 폭이 안의 목록(360px) 폭에 맞춰 376px. - 390×844: 열린 Viewport 폭 378px, 좌측 16px에서 시작해 오른쪽 끝에 닿음(가로 overflow 없음). 한계는 §11. - 미확인: 닫힘 animation 시각, `viewport={false}` 구성의 열림(코드 경로는 Radix/shadcn 기본), 두 Content 사이 이동 animation 시각, ArrowDown으로 Content 진입.