Lzh on GitHub

Navigation Menu

用于导航网站的链接集合。
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
  NavigationMenuContent,
  NavigationMenuIndicator,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuRoot,
  NavigationMenuTrigger,
  NavigationMenuViewport,
} from 'reka-ui'
import { ref } from 'vue'
import NavigationMenuListItem from './NavigationMenuListItem.vue'

const currentTrigger = ref('')
</script>

<template>
  <NavigationMenuRoot
    v-model="currentTrigger"
    class="relative z-[1] flex w-full justify-center"
  >
    <NavigationMenuList class="center m-0 flex list-none rounded-lg bg-white p-1 shadow-sm border">
      <NavigationMenuItem>
        <NavigationMenuTrigger
          class="text-grass11 hover:bg-green3 focus:shadow-green7 group flex select-none items-center justify-between gap-[2px] rounded-[4px] px-3 py-2 text-sm font-medium leading-none outline-none focus:shadow-[0_0_0_2px]"
        >
          Learn
          <Icon
            icon="radix-icons:caret-down"
            class="text-green10 relative top-[1px] transition-transform duration-[150ms] ease-in group-data-[state=open]:-rotate-180"
          />
        </NavigationMenuTrigger>
        <NavigationMenuContent
          class="data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute top-0 left-0 w-full sm:w-auto"
        >
          <ul class="one m-0 grid list-none gap-x-[10px] p-[22px] sm:w-[500px] sm:grid-cols-[0.75fr_1fr]">
            <li class="row-span-3 grid">
              <NavigationMenuLink as-child>
                <a
                  class="focus:shadow-green7 bg-stone-900 flex h-full w-full select-none flex-col justify-end rounded-lg p-[25px] no-underline outline-none focus:shadow-[0_0_0_2px]"
                  href="/"
                >
                  <img
                    class="w-16"
                    src="https://reka-ui.com/logo.svg"
                  >
                  <div class="mt-4 mb-[7px] text-[18px] font-medium leading-[1.2] text-white">Reka UI</div>
                  <p class="text-mauve4 text-[14px] leading-[1.3]">Unstyled, accessible components for Vue.</p>
                </a>
              </NavigationMenuLink>
            </li>

            <NavigationMenuListItem
              href="https://stitches.dev/"
              title="Stitches"
            >
              CSS-in-JS with best-in-class developer experience.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              href="/colors"
              title="Colors"
            >
              Beautiful, thought-out palettes with auto dark mode.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              href="https://lucide.dev/icons/"
              title="Icons"
            >
              A crisp set of 15x15 icons, balanced and consistent.
            </NavigationMenuListItem>
          </ul>
        </NavigationMenuContent>
      </NavigationMenuItem>

      <NavigationMenuItem>
        <NavigationMenuTrigger
          class="text-grass11 hover:bg-green3 focus:shadow-green7 group flex select-none items-center justify-between gap-[2px] rounded-[4px] px-3 py-2 text-sm font-medium leading-none outline-none focus:shadow-[0_0_0_2px]"
        >
          Overview
          <Icon
            icon="radix-icons:caret-down"
            class="text-green10 relative top-[1px] transition-transform duration-[150ms] ease-in group-data-[state=open]:-rotate-180"
          />
        </NavigationMenuTrigger>
        <NavigationMenuContent class="data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute top-0 left-0 w-full sm:w-auto">
          <ul class="m-0 grid list-none gap-x-[10px] p-[22px] sm:w-[600px] sm:grid-flow-col sm:grid-rows-3">
            <NavigationMenuListItem
              title="Introduction"
              href="/docs/primitives/overview/introduction"
            >
              Build high-quality, accessible design systems and web apps.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              title="Getting started"
              href="/docs/primitives/overview/getting-started"
            >
              A quick tutorial to get you up and running with Reka UI.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              title="Styling"
              href="/docs/primitives/guides/styling"
            >
              Unstyled and compatible with any styling solution.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              title="Animation"
              href="/docs/primitives/guides/animation"
            >
              Use CSS keyframes or any animation library of your choice.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              title="Accessibility"
              href="/docs/primitives/overview/accessibility"
            >
              Tested in a range of browsers and assistive technologies.
            </NavigationMenuListItem>
            <NavigationMenuListItem
              title="Releases"
              href="/docs/primitives/overview/releases"
            >
              Reka UI releases and their changelogs.
            </NavigationMenuListItem>
          </ul>
        </NavigationMenuContent>
      </NavigationMenuItem>

      <NavigationMenuItem>
        <NavigationMenuLink
          class="text-grass11 hover:bg-green3 focus:shadow-green7 block select-none rounded-[4px] px-3 py-2 text-sm font-medium leading-none no-underline outline-none focus:shadow-[0_0_0_2px]"
          href="https://github.com/unovue/reka-ui"
        >
          GitHub
        </NavigationMenuLink>
      </NavigationMenuItem>

      <NavigationMenuIndicator
        class="absolute data-[state=hidden]:opacity-0 duration-200 data-[state=visible]:animate-fadeIn data-[state=hidden]:animate-fadeOut top-full w-[--reka-navigation-menu-indicator-size] translate-x-[--reka-navigation-menu-indicator-position] mt-[1px] z-[100] flex h-[10px] items-end justify-center overflow-hidden transition-[all,transform_250ms_ease]"
      >
        <div class="relative top-[70%] h-[12px] w-[12px] rotate-[45deg] bg-white border" />
      </NavigationMenuIndicator>
    </NavigationMenuList>

    <div class="perspective-[2000px] absolute top-full left-0 flex w-full justify-center ">
      <NavigationMenuViewport
        class="data-[state=open]:animate-scaleIn data-[state=closed]:animate-scaleOut relative mt-[10px] h-[var(--reka-navigation-menu-viewport-height)] w-full origin-[top_center] overflow-hidden rounded-xl bg-white transition-[width,_height] duration-300 sm:w-[var(--reka-navigation-menu-viewport-width)] border shadow-sm"
      />
    </div>
  </NavigationMenuRoot>
</template>

功能特点

  • 可控或不可控。
  • 灵活的布局结构,支持 Tab 焦点管理。
  • 支持子菜单。
  • 可选的活动项指示器。
  • 完整的键盘导航。
  • 暴露 CSS 变量以实现高级动画。
  • 支持自定义时间。

安装

从命令行安装组件。

$ npm add reka-ui

结构

导入所有部分并将其组合在一起。

<script setup lang="ts">
  import {
    NavigationMenuContent,
    NavigationMenuIndicator,
    NavigationMenuItem,
    NavigationMenuLink,
    NavigationMenuList,
    NavigationMenuRoot,
    NavigationMenuSub,
    NavigationMenuTrigger,
    NavigationMenuViewport,
  } from 'reka-ui'
</script>

<template>
  <NavigationMenuRoot>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger />
        <NavigationMenuContent>
          <NavigationMenuLink />
        </NavigationMenuContent>
      </NavigationMenuItem>
      
      <NavigationMenuItem>
        <NavigationMenuLink />
      </NavigationMenuItem>
      
      <NavigationMenuItem>
        <NavigationMenuTrigger />
        <NavigationMenuContent>
          <NavigationMenuSub>
            <NavigationMenuList />
            <NavigationMenuViewport />
          </NavigationMenuSub>
        </NavigationMenuContent>
      </NavigationMenuItem>
      
      <NavigationMenuIndicator />
    </NavigationMenuList>
    
    <NavigationMenuViewport />
  </NavigationMenuRoot>
</template>

API 参考

Root

包含导航菜单的所有部分。

属性默认值类型描述
as'nav'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
defaultValuestring首次渲染时应激活的菜单项的值。当您不需要控制值状态时使用。
delayDuration200number指针进入触发器到工具提示打开的持续时间。
dir'ltr' | 'rtl'组合框(如果适用)的阅读方向。如果省略,则从 ConfigProvider 全局继承或假定为从左到右 (LTR) 阅读模式。
disableClickTriggerfalseboolean如果为 true,菜单无法通过点击触发器打开。
disableHoverTriggerfalseboolean如果为 true,菜单无法通过悬停触发器打开。
disablePointerLeaveClosefalseboolean如果为 true,菜单在指针离开事件期间不会关闭。
modelValuestring要激活的菜单项的受控值。可用作 v-model
orientation'horizontal''vertical' | 'horizontal'菜单的方向。
skipDelayDuration300number用户在不再次产生延迟的情况下进入另一个触发器所需的时间。
unmountOnHidetrueboolean当为 true 时,元素将在关闭状态下卸载。

触发事件 (Emit)

事件名称Payload描述
update:modelValue[value: string]值改变时调用的事件处理程序。

默认插槽

插槽参数插槽参数类型描述
modelValuestring当前输入值

数据属性

属性
[data-orientation]"vertical" | "horizontal"

Sub

表示一个子菜单。嵌套时用它代替根部分来创建子菜单。

属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
defaultValuestring首次渲染时应激活的菜单项的值。当您不需要控制值状态时使用。
modelValuestring要激活的子菜单项的受控值。可用作 v-model
orientation'horizontal''vertical' | 'horizontal'菜单的方向。

触发事件 (Emit)

事件名称Payload描述
update:modelValue[value: string]值改变时调用的事件处理程序。

默认插槽

插槽参数插槽参数类型描述
modelValuestring当前输入值

数据属性

属性
[data-orientation]"vertical" | "horizontal"

List

包含顶级菜单项。

属性默认值类型描述
as'ul'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南

数据属性

属性
[data-orientation]"vertical" | "horizontal"

Item

一个顶级菜单项,包含链接或触发器和内容的组合。

属性默认值类型描述
as'li'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
valuestring当导航菜单受控时,将项目与活动值关联的唯一值。当不受控时,此 prop 会自动管理。

Trigger

切换内容的按钮。

属性默认值类型描述
as'button'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
disabledfalseboolean当为 true 时,阻止用户与项目交互。

数据属性

属性
[data-state]"open" | "closed"
[data-disabled]禁用时存在

Content

包含与每个触发器相关联的内容。

使用 Presence 组件构建 - 支持任何动画技术,同时保持对存在(presence)触发事件的访问。
属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
disableOutsidePointerEventsfalseboolean当为 true 时,DismissableLayer 外部的悬停/焦点/点击交互将被禁用。用户需要两次点击外部元素才能与其交互:一次关闭 DismissableLayer,再次触发元素。
forceMountfalseboolean当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

触发事件 (Emit)

事件名称Payload描述
escapeKeyDown[event: KeyboardEvent]Escape 键按下时调用的事件处理程序。可以阻止。
focusOutside[event: FocusOutsideEvent]焦点移出 DismissableLayer 时调用的事件处理程序。可以阻止。
interactOutside[event: PointerDownOutsideEvent | FocusOutsideEvent]DismissableLayer 外部发生交互时调用的事件处理程序。具体来说,当 pointerdown 事件发生在外部或焦点移出时。可以阻止。
pointerDownOutside[event: PointerDownOutsideEvent]pointerdown 事件发生在 DismissableLayer 外部时调用的事件处理程序。可以阻止。

数据属性

属性
[data-state]"open" | "closed"
[data-motion]"to-start" | "to-end" | "from-start" | "from-end"
[data-orientation]"vertical" | "horizontal"

一个导航链接。

属性默认值类型描述
activefalseboolean用于标识链接为当前活动页面。
as'a'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南

触发事件 (Emit)

事件名称Payload描述
select[payload: CustomEvent<{ originalEvent: Event; }>]用户选择链接(通过鼠标或键盘)时调用的事件处理程序。在此处理程序中调用 event.preventDefault 将阻止在选择该链接时关闭导航菜单。

数据属性

属性
[data-active]活动时存在

Indicator

一个可选的指示器元素,渲染在列表下方,用于突出显示当前活动的触发器。

使用 Presence 组件构建 - 支持任何动画技术,同时保持对存在(presence)触发事件的访问。
属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
forceMountfalseboolean当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

数据属性

属性
[data-state]"visible" | "hidden"
[data-orientation]"vertical" | "horizontal"

CSS 变量

变量描述
--reka-navigation-menu-indicator-size指示器的大小
--reka-navigation-menu-indicator-position指示器的位置

Viewport

一个可选的视口元素,用于在列表之外渲染活动内容。

使用 Presence 组件构建 - 支持任何动画技术,同时保持对存在(presence)触发事件的访问。
属性默认值类型描述
align'center''start' | 'center' | 'end'视口对齐方式,用于 CSS 变量 (--reka-navigation-menu-viewport-left, --reka-navigation-menu-viewport-top)。
as'div'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildfalseboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的 props 和行为。有关详细信息,请阅读我们的组合指南
forceMountfalseboolean当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

数据属性

属性
[data-state]"visible" | "hidden"
[data-orientation]"vertical" | "horizontal"

CSS 变量

变量描述
--reka-navigation-menu-viewport-width视口可见/隐藏时的宽度,根据活动内容计算
--reka-navigation-menu-viewport-height视口可见/隐藏时的高度,根据活动内容计算

示例

垂直方向

您可以使用 orientation prop 创建一个垂直菜单。

<script setup lang="ts">
  import {
    NavigationMenuContent,
    NavigationMenuIndicator,
    NavigationMenuItem,
    NavigationMenuLink,
    NavigationMenuList,
    NavigationMenuRoot,
    NavigationMenuSub,
    NavigationMenuTrigger,
    NavigationMenuViewport,
  } from 'reka-ui'
</script>

<template>
  <NavigationMenuRoot orientation="vertical">
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item one</NavigationMenuTrigger>
        <NavigationMenuContent>Item one content</NavigationMenuContent>
      </NavigationMenuItem>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item two</NavigationMenuTrigger>
        <NavigationMenuContent>Item Two content</NavigationMenuContent>
      </NavigationMenuItem>
    </NavigationMenuList>
  </NavigationMenuRoot>
</template>

灵活布局

当您需要额外控制 Content 渲染位置时,请使用 Viewport 部分。当您的设计需要调整 DOM 结构或需要灵活性以实现高级动画时,这会很有帮助。Tab 焦点将自动保持。

<script setup lang="ts">
  import {
    NavigationMenuContent,
    NavigationMenuItem,
    NavigationMenuList,
    NavigationMenuRoot,
    NavigationMenuTrigger,
    NavigationMenuViewport,
  } from 'reka-ui'
</script>

<template>
  <NavigationMenuRoot>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item one</NavigationMenuTrigger>
        <NavigationMenuContent>Item one content</NavigationMenuContent>
      </NavigationMenuItem>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item two</NavigationMenuTrigger>
        <NavigationMenuContent>Item two content</NavigationMenuContent>
      </NavigationMenuItem>
    </NavigationMenuList>
    <NavigationMenuViewport />
  </NavigationMenuRoot>
</template>

使用指示器

您可以使用可选的 Indicator 部分来突出显示当前活动的 Trigger,这在您想要提供动画视觉提示(例如箭头或高亮)来伴随 Viewport 时很有用。

<script setup lang="ts">
  import {
    NavigationMenuContent,
    NavigationMenuItem,
    NavigationMenuList,
    NavigationMenuRoot,
    NavigationMenuTrigger,
    NavigationMenuViewport,
  } from 'reka-ui'
</script>

<template>
  <NavigationMenuRoot>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item one</NavigationMenuTrigger>
        <NavigationMenuContent>Item one content</NavigationMenuContent>
      </NavigationMenuItem>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item two</NavigationMenuTrigger>
        <NavigationMenuContent>Item two content</NavigationMenuContent>
      </NavigationMenuItem>
      
      <NavigationMenuIndicator class="NavigationMenuIndicator" />
    </NavigationMenuList>
    
    <NavigationMenuViewport />
  </NavigationMenuRoot>
</template>
styles.css
.NavigationMenuIndicator {
  background-color: grey;
  position: absolute;
  transition: width, transform, 250ms ease;
}

.NavigationMenuIndicator[data-orientation="horizontal"] {
  left: 0;
  height: 3px;
  transform: translateX(var(--reka-navigation-menu-indicator-position));
  width: var(--reka-navigation-menu-indicator-size);
}

使用子菜单

通过嵌套您的 NavigationMenu 并使用 Sub 部分代替其 Root 来创建子菜单。子菜单与 Root 导航菜单的工作方式不同,它们类似于 Tabs,即始终应有一个项目处于活动状态,因此请务必分配并设置 defaultValue

<script setup lang="ts">
  import {
    NavigationMenuContent,
    NavigationMenuItem,
    NavigationMenuList,
    NavigationMenuRoot,
    NavigationMenuSub,
    NavigationMenuTrigger,
    NavigationMenuViewport,
  } from 'reka-ui'
</script>

<template>
  <NavigationMenuRoot>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item one</NavigationMenuTrigger>
        <NavigationMenuContent>Item one content</NavigationMenuContent>
      </NavigationMenuItem>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item two</NavigationMenuTrigger>
        <NavigationMenuContent>
          <NavigationMenuSub default-value="sub1">
            <NavigationMenuList>
              <NavigationMenuItem value="sub1">
                <NavigationMenuTrigger>Sub item one</NavigationMenuTrigger>
                <NavigationMenuContent> Sub item one content </NavigationMenuContent>
              </NavigationMenuItem>
              <NavigationMenuItem value="sub2">
                <NavigationMenuTrigger>Sub item two</NavigationMenuTrigger>
                <NavigationMenuContent> Sub item two content </NavigationMenuContent>
              </NavigationMenuItem>
            </NavigationMenuList>
          </NavigationMenuSub>
        </NavigationMenuContent>
      </NavigationMenuItem>
    </NavigationMenuList>
  </NavigationMenuRoot>
</template>

使用客户端路由

如果您需要使用路由包提供的 RouterLink 组件,我们建议在 NavigationMenuLink 上添加 asChild="true" 或设置 as="RouterLink"。这将确保保持可访问性和一致的键盘控制:

<script setup lang="ts">
  import { NavigationMenuItem, NavigationMenuList, NavigationMenuRoot } from 'reka-ui'

  // RouterLink should be injected by default if using `vue-router`
</script>

<template>
  <NavigationMenuRoot>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuLink as-child>
          <RouterLink to="/">
            Home
          </RouterLink>
          <NavigationMenuLink />
        </NavigationMenuLink>
      </NavigationMenuItem>
      <NavigationMenuItem>
        <NavigationMenuLink
          :as="RouterLink"
          to="/about"
        >
          About
        </NavigationMenuLink>
      </NavigationMenuItem>
    </NavigationMenuList>
  </NavigationMenuRoot>
</template>

高级动画

我们暴露了 --reka-navigation-menu-viewport-[width|height]data-motion['from-start'|'to-start'|'from-end'|'to-end'] 属性,允许您根据进入/退出方向动画 Viewport 大小和 Content 位置。

将这些与 position: absolute; 结合使用,可以创建在项目之间移动时平滑重叠的动画效果。

<script setup lang="ts">
  import {
    NavigationMenuContent,
    NavigationMenuItem,
    NavigationMenuList,
    NavigationMenuRoot,
    NavigationMenuTrigger,
    NavigationMenuViewport,
  } from 'reka-ui'
</script>

<template>
  <NavigationMenuRoot>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item one</NavigationMenuTrigger>
        <NavigationMenuContent class="NavigationMenuContent">
          Item one content
        </NavigationMenuContent>
      </NavigationMenuItem>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item two</NavigationMenuTrigger>
        <NavigationMenuContent class="NavigationMenuContent">
          Item two content
        </NavigationMenuContent>
      </NavigationMenuItem>
    </NavigationMenuList>
    
    <NavigationMenuViewport class="NavigationMenuViewport" />
  </NavigationMenuRoot>
</template>
styles.css
.NavigationMenuContent {
  position: absolute;
  top: 0;
  left: 0;
  animation-duration: 250ms;
  animation-timing-function: ease;
}

.NavigationMenuContent[data-motion="from-start"] {
  animation-name: enterFromLeft;
}

.NavigationMenuContent[data-motion="from-end"] {
  animation-name: enterFromRight;
}

.NavigationMenuContent[data-motion="to-start"] {
  animation-name: exitToLeft;
}

.NavigationMenuContent[data-motion="to-end"] {
  animation-name: exitToRight;
}

.NavigationMenuViewport {
  position: relative;
  width: var(--reka-navigation-menu-viewport-width);
  height: var(--reka-navigation-menu-viewport-height);
  transition: width, height, 250ms ease;
}

@keyframes enterFromRight {
  from {
    opacity: 0;
    transform: translateX(200px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes enterFromLeft {
  from {
    opacity: 0;
    transform: translateX(-200px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes exitToRight {
  from {
    opacity: 1;
    transform: translateX(0);
  }
  to {
    opacity: 0;
    transform: translateX(200px);
  }
}

@keyframes exitToLeft {
  from {
    opacity: 1;
    transform: translateX(0);
  }
  to {
    opacity: 0;
    transform: translateX(-200px);
  }
}

可访问性

遵循 导航 角色要求。

与菜单栏的区别

NavigationMenu 不应与 menubar 混淆,尽管此原语在口语中也使用“菜单”一词来指代一组导航链接,但它不使用 WAI-ARIA menu 角色。这是因为 menumenubars 的行为更像桌面应用程序窗口中最常见的原生操作系统菜单,因此它们具有复杂的功能,如复合焦点管理和首字符导航。 这些功能通常被认为对于网站导航不必要,最坏情况是会混淆熟悉既定网站模式的用户。 有关更多信息,请参阅 W3C 披露导航菜单 示例。

链接使用和 aria-current

在菜单中,所有导航链接都必须使用 NavigationMenuLink,这不仅适用于主列表,也适用于通过 NavigationMenuContent 渲染的任何内容。这将确保一致的键盘交互和可访问性,同时还可以访问 active prop 以设置 aria-current 和活动样式。有关与第三方路由组件一起使用的更多信息,请参阅此示例

键盘交互

按键描述
Space当焦点在 NavigationMenuTrigger 上时,打开内容。
Enter当焦点在 NavigationMenuTrigger 上时,打开内容。
Tab将焦点移动到下一个可聚焦元素。
ArrowDownhorizontal 且焦点在打开的 NavigationMenuTrigger 上时,将焦点移入 NavigationMenuContent。将焦点移到下一个 NavigationMenuTriggerNavigationMenuLink
ArrowUp将焦点移到上一个 NavigationMenuTriggerNavigationMenuLink
ArrowRight ArrowLeftvertical 且焦点在打开的 NavigationMenuTrigger 上时,将焦点移入其 NavigationMenuContent。将焦点移到下一个/上一个 NavigationMenuTriggerNavigationMenuLink
Home End将焦点移到第一个/最后一个 NavigationMenu.TriggerNavigationMenu.Link
Esc关闭打开的 NavigationMenu.Content 并将焦点移至其 NavigationMenu.Trigger