跳转到内容

NavigationMenu 导航菜单 ​

用于站点顶部的多级导航菜单。

用法 ​

从顶部入口展开站点导航

组件结构 ​

加粗的是必需部件。

data-scope="navigation-menu":root · list · item · trigger · trigger-indicator · content · link · indicator · viewport

示例 ​

竖向排列 ​

在侧栏旁展开子级导航

直达链接 ​

混合下拉入口与普通链接

共享面板 ​

在固定位置切换不同导航内容

设计指引 ​

何时使用 ​

  • 门户、营销站或文档站具有多组导航链接。

何时不用 ​

特性 ​

  • 支持横向和竖向排列、延迟展开与键盘导航。
  • 没有子级的入口可直接渲染为链接。
  • viewport 可让所有面板在同一位置切换。
  • 当前链接使用 aria-current="page",并自带一条静态指示线(横排的直达链接贴底边,竖排与面板里的链接贴起始缘);indicator 部件指的是开着的面板,两者各说各的。

组合 ​

  • 窄屏时切换为抽屉或侧栏导航,不压缩顶部入口。

最佳实践 ​

  • 使用短标题和简洁说明组织链接。
  • 保留默认展开延时,避免指针经过时连续闪动。

反模式 ​

  • 不要在导航面板中放置表单或一次性命令。
  • 不要在窄屏中强行保留完整横向导航。

API 参考 ​

产物 ​

层值
自定义元素<xh-navigation-menu>
Vue 组件XhNavigationMenuContent XhNavigationMenuIndicator XhNavigationMenuItem XhNavigationMenuLink XhNavigationMenuList XhNavigationMenuRoot XhNavigationMenuTrigger XhNavigationMenuTriggerIndicator XhNavigationMenuViewport
组合式函数useNavigationMenu
状态机navigationMenuMachine
皮肤@xihan-ui/styles/navigation-menu.css

Props ​

属性类型必填说明
collectionNavigationMenuNode[]入口数据,入口文本与禁用的事实源。提供后 trigger 部件只需声明 value。 未提供时回到文本与禁用都写在部件上的方式。
valuestring | null当前展开项,提供即受控;null 表示全部收起。
defaultValuestring | null
orientationOrientation方向键轴向,默认 horizontal。
delayDurationnumber悬停 / 聚焦到 trigger 后等待多久才展开,默认 200ms。
skipDelayDurationnumber收起之后的静默窗口,默认 300ms;窗口内再次触及任意 trigger 直接展开。
dirDirection文字方向,默认 ltr。
loopboolean方向键到达末尾是否回绕,默认 true。
disabledboolean整套导航禁用:所有入口都为 aria-disabled,面板不再展开。
translationsPartial<NavigationMenuTranslations>
toneTone语气:brand / neutral / success / warning / danger / info,决定使用哪族颜色。
sizeSize尺寸:sm / md / lg。
onValueChange(details: NavigationMenuValueChangeDetails) => voidvalue 变化回调。

collection 的元素。

字段类型必填说明
valuestring是
labelstring入口文本;默认回退为 value。
disabledboolean入口禁用:方向键跳过它,但它仍可聚焦、仍是导航起点。
hrefstring直达目标。提供后该项即为一条链接,没有面板。
currentboolean指向当前页面的直达入口:输出 aria-current="page"。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
value-changeNavigationMenuValueChangeDetails展开项变化;detail 为 { value: string | null }

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhNavigationMenuContentvaluestring是
XhNavigationMenuLinkcurrentboolean
XhNavigationMenuRootrenderPanel(node: NavigationMenuNodeMeta) => ReactNode每张面板的内容;只提供 collection 时由它承载。
XhNavigationMenuRootchildrenReactNode
XhNavigationMenuTriggervaluestring是
XhNavigationMenuTriggerdisabledboolean默认交给 connect 查询 collection,写死 false 会覆盖数据中的禁用。
XhNavigationMenuTriggerIndicatorvaluestring是
XhNavigationMenuTriggerIndicatordisabledboolean

状态 ​

公开状态写入 data-state。

部件取值
root'open' | 'closed'
trigger'open' | 'closed'
trigger-indicator'open' | 'closed'
content'open' | 'closed'
indicator'open' | 'closed'
viewport'open' | 'closed'

以下名称仅用于内部状态机。

状态:idle · opening · skipping

事件:TRIGGER.POINTER · TRIGGER.FOCUS · TRIGGER.TOGGLE · DISMISS · VALUE.SET · PRESENCE.SET · after.delayDuration · after.skipDelayDuration · PRESS.START · PRESS.END

判据:hasValue · isCurrent · shouldKeepOpen · canPress

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
valuestring | null当前展开的项;全部收起时为 null。
collectionreadonly NavigationMenuNodeMeta[]由 collection 推导的入口元信息,按数据顺序排列;未提供 collection 时为空数组。
openboolean是否有面板展开。
isOpen(value: string) => boolean
setValue(next: string | null) => void
getRootProps() => T['element']
getListProps() => T['element']
getItemProps() => T['element']
getTriggerProps(props: NavigationMenuTriggerProps) => T['button']
getTriggerIndicatorProps(props: NavigationMenuTriggerProps) => T['element']
getContentProps(props: NavigationMenuContentProps) => T['element']
getLinkProps(props: NavigationMenuLinkProps) => T['element']
getIndicatorProps() => T['element']
getViewportProps() => T['element']

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Enter / Spaceheld on trigger / link, 导航未禁用且入口未禁用按住期间入口或面板链接投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,链接随面板收起一并撤下。开合与激活语义照旧由这一次按键承担
ArrowRight / ArrowDownfocus in trigger, 按键与 orientation 同轴焦点移到下一个 trigger(禁用项跳过、尽头按 loop 回绕);随后的自动展开走 delayDuration
ArrowLeft / ArrowUpfocus in trigger, 按键与 orientation 同轴焦点移到上一个 trigger
Homefocus in trigger焦点移到首个可停留 trigger
Endfocus in trigger焦点移到末个可停留 trigger
Enter / Spacefocus in trigger, not disabled立即展开对应面板(不走 delayDuration);面板是自动弹出来的那一次不收起,再按一次才收起
Escapeopen收起面板并把焦点归还对应 trigger;静默窗口内这一次归还不会把面板重新弹出来
Tab / Shift+Tabopen, focus in trigger走进展开的面板:面板就在 trigger 之后,收起的面板带 hidden 因而被整个跳过

ARIA ​

以下属性由 connect 生成。

部件属性值
rootaria-labelprops.translations.root
triggeraria-controlscontent 部件的 id
triggeraria-disabled'true' | 'false'
triggeraria-expanded'true' | 'false'
trigger-indicatoraria-hidden'true'
contentaria-hidden!isOpen || undefined
contentaria-labelledbytrigger 部件的 id
contentrole'group'
linkaria-current'page' | undefined
indicatoraria-hidden'true'
viewportaria-hidden!open || undefined

样式参考 ​

皮肤 ​

@xihan-ui/styles/navigation-menu.css 使用 [data-scope="navigation-menu"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides。

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-orientationprops.orientation
rootdata-sizeprops.size
rootdata-state'open' | 'closed'
rootdata-toneprops.tone
listdata-orientationprops.orientation
triggerdata-disabled''(条件成立时才出现)
triggerdata-in-path''(条件成立时才出现)
triggerdata-orientationprops.orientation
triggerdata-pressed''(条件成立时才出现)
triggerdata-state'open' | 'closed'
triggerdata-xh-collection-context'nav'
triggerdata-xh-collection-item''
triggerdata-xh-collection-sizeprops.size
trigger-indicatordata-disabled''(条件成立时才出现)
trigger-indicatordata-orientationprops.orientation
trigger-indicatordata-state'open' | 'closed'
contentdata-orientationprops.orientation
contentdata-state'open' | 'closed'
linkdata-current''(条件成立时才出现)
linkdata-pressed''(条件成立时才出现)
linkdata-xh-collection-context'nav'
linkdata-xh-collection-item''
linkdata-xh-collection-sizeprops.size
indicatordata-orientationprops.orientation
indicatordata-state'open' | 'closed'
indicatordata-valuecontext.get('value')
viewportdata-orientationprops.orientation
viewportdata-state'open' | 'closed'

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-navigation-menu-content-bgcontent
viewport
backgrounddefault--xh-bg-surfacenavigation-menu 的 content、viewport 部件 background 覆盖槽。
--xh-navigation-menu-content-bordercontent
viewport
borderdefault--xh-border-defaultnavigation-menu 的 content、viewport 部件 border 覆盖槽。
--xh-navigation-menu-content-gapcontentgapdefault--xh-space-1navigation-menu 的 content 部件 gap 覆盖槽。
--xh-navigation-menu-content-min-wcontentmin-inline-sizedefault--xh-overlay-menu-min-wnavigation-menu 的 content 部件 min-inline-size 覆盖槽。
--xh-navigation-menu-content-offsetcontent
viewport
inset-block-start
inset-inline-start
default
orientation=vertical
--xh-space-1navigation-menu 的 content、viewport 部件 inset-block-start、inset-inline-start 覆盖槽。
--xh-navigation-menu-content-pcontentpaddingdefault--xh-surface-pad-xsnavigation-menu 的 content 部件 padding 覆盖槽。
--xh-navigation-menu-content-radiuscontent
viewport
border-radiusdefault--xh-shape-overlaynavigation-menu 的 content、viewport 部件 border-radius 覆盖槽。
--xh-navigation-menu-content-shadowcontent
viewport
box-shadowdefault--xh-elevation-floatingnavigation-menu 的 content、viewport 部件 box-shadow 覆盖槽。
--xh-navigation-menu-fgrootcolordefault--xh-fg-defaultnavigation-menu 的 root 部件 color 覆盖槽。
--xh-navigation-menu-font-sizeroot
trigger
font-sizedefault--xh-_navigation-menu-font-sizenavigation-menu 的 root、trigger 部件 font-size 覆盖槽。
--xh-navigation-menu-gaplistgapdefault--xh-space-1navigation-menu 的 list 部件 gap 覆盖槽。
--xh-navigation-menu-icon-sizeroot--xh-icon-sizedefault
size=lg
size=sm
--xh-glyph-size-lg
--xh-glyph-size-md
--xh-glyph-size-sm
navigation-menu 的 root 部件 --xh-icon-size 覆盖槽。
--xh-navigation-menu-indicator-colorindicator
link
backgroundcurrent
default
--xh-_navigation-menu-accentnavigation-menu 的 indicator、link 部件 background 覆盖槽。
--xh-navigation-menu-indicator-radiusindicator
link
border-radiuscurrent
default
--xh-shape-pillnavigation-menu 的 indicator、link 部件 border-radius 覆盖槽。
--xh-navigation-menu-indicator-thicknessindicator
item
link
list
block-size
inline-size
inset-block-end
current
default
orientation=horizontal
orientation=vertical
--xh-stroke-thicknavigation-menu 的 indicator、item、link、list 部件 block-size、inline-size、inset-block-end 覆盖槽。
--xh-navigation-menu-layercontent
viewport
z-indexdefault--xh-_layernavigation-menu 的 content、viewport 部件 z-index 覆盖槽。
--xh-navigation-menu-link-bg-hoverlinkbackground-colordisabled
error
highlighted
hover
is(:focus-visible, [data-highlighted])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
xh-collection-context=nav
--xh-_navigation-menu-highlight-bgnavigation-menu 的 link 部件 background-color 覆盖槽。
--xh-navigation-menu-link-bg-pressedlinkbackground-colordisabled
error
is(:active, [data-pressed])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
xh-collection-context=nav
--xh-bg-subtle-hovernavigation-menu 的 link 部件 background-color 覆盖槽。
--xh-navigation-menu-link-fglinkcolordefault
disabled
error
highlighted
hover
is(:active, [data-pressed])
is(:focus-visible, [data-highlighted])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
xh-collection-context=nav
--xh-fg-defaultnavigation-menu 的 link 部件 color 覆盖槽。
--xh-navigation-menu-link-fg-currentlinkcolorcurrent
disabled
error
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
xh-collection-context=nav
--xh-fg-brand-strongnavigation-menu 的 link 部件 color 覆盖槽。
--xh-navigation-menu-link-font-sizelinkfont-sizedefault--xh-_navigation-menu-link-font-sizenavigation-menu 的 link 部件 font-size 覆盖槽。
--xh-navigation-menu-link-font-weight-currentlinkfont-weightcurrent
disabled
error
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
xh-collection-context=nav
--xh-font-weight-mediumnavigation-menu 的 link 部件 font-weight 覆盖槽。
--xh-navigation-menu-link-pxlinkpadding-inlinedefault--xh-_navigation-menu-link-pxnavigation-menu 的 link 部件 padding-inline 覆盖槽。
--xh-navigation-menu-link-pylinkpadding-blockdefault--xh-_navigation-menu-link-pynavigation-menu 的 link 部件 padding-block 覆盖槽。
--xh-navigation-menu-link-radiuslinkborder-radiusdefault--xh-shape-controlnavigation-menu 的 link 部件 border-radius 覆盖槽。
--xh-navigation-menu-trigger-bg-activetriggerbackground-colorin-path
xh-collection-context=nav
--xh-_navigation-menu-highlight-bgnavigation-menu 的 trigger 部件 background-color 覆盖槽。
--xh-navigation-menu-trigger-bg-hovertriggerbackground-colordisabled
error
hover
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
xh-collection-context=nav
--xh-_navigation-menu-highlight-bgnavigation-menu 的 trigger 部件 background-color 覆盖槽。
--xh-navigation-menu-trigger-bg-pressedtriggerbackground-colordisabled
error
is(:active, [data-pressed])
not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error])
pressed
xh-collection-context=nav
--xh-bg-subtle-hovernavigation-menu 的 trigger 部件 background-color 覆盖槽。
--xh-navigation-menu-trigger-fgtriggercolordefault
xh-collection-context=nav
--xh-fg-mutednavigation-menu 的 trigger 部件 color 覆盖槽。
--xh-navigation-menu-trigger-font-weighttriggerfont-weightdefault
xh-collection-context=nav
--xh-font-weight-regularnavigation-menu 的 trigger 部件 font-weight 覆盖槽。
--xh-navigation-menu-trigger-gaptriggergapdefault--xh-_navigation-menu-trigger-gapnavigation-menu 的 trigger 部件 gap 覆盖槽。
--xh-navigation-menu-trigger-htriggerblock-sizedefault--xh-_navigation-menu-trigger-hnavigation-menu 的 trigger 部件 block-size 覆盖槽。
--xh-navigation-menu-trigger-pxtriggerpadding-inlinedefault--xh-_navigation-menu-trigger-pxnavigation-menu 的 trigger 部件 padding-inline 覆盖槽。
--xh-navigation-menu-trigger-radiustriggerborder-radiusdefault--xh-shape-controlnavigation-menu 的 trigger 部件 border-radius 覆盖槽。
--xh-navigation-menu-viewport-pviewportpaddingdefault--xh-space-2navigation-menu 的 viewport 部件 padding 覆盖槽。

动效 ​

动效角色:按压 · 状态 · 切换 · 指示与换位 · 出现(锚定面板) · 出现(无锚定弹出)(见动效规范)。

共享关键帧 xh-pop-in · xh-pop-out 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;block-size · inline-size · rotate · translate 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

皮肤之外还有一段:退场由适配器的退场闸门把关,动画播完才真收起。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像;另有按 dir 分支的规则。

Released under The MIT License