组件

导航菜单

用于网站导航的链接集合。

import * as React from "react";
import { NavigationMenu } from "radix-ui";
import classNames from "classnames";
import { CaretDownIcon } from "@radix-ui/react-icons";
import "./styles.css";
const NavigationMenuDemo = () => {
return (
<NavigationMenu.Root className="NavigationMenuRoot">
<NavigationMenu.List className="NavigationMenuList">
<NavigationMenu.Item>
<NavigationMenu.Trigger className="NavigationMenuTrigger">
Learn <CaretDownIcon className="CaretDown" aria-hidden />
</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
<ul className="List one">
<li style={{ gridRow: "span 3" }}>
<NavigationMenu.Link asChild>
<a className="Callout" href="/">
<svg aria-hidden width="38" height="38" viewBox="0 0 25 25" fill="white" >
<path d="M12 25C7.58173 25 4 21.4183 4 17C4 12.5817 7.58173 9 12 9V25Z"></path>
<path d="M12 0H4V8H12V0Z"></path>
<path d="M17 8C19.2091 8 21 6.20914 21 4C21 1.79086 19.2091 0 17 0C14.7909 0 13 1.79086 13 4C13 6.20914 14.7909 8 17 8Z"></path>
</svg>
<div className="CalloutHeading">Radix Primitives</div>
<p className="CalloutText">
Unstyled, accessible components for React.
</p>
</a>
</NavigationMenu.Link>
</li>
<ListItem href="https://stitches.dev/" title="Stitches">
CSS-in-JS with best-in-class developer experience.
</ListItem>
<ListItem href="/colors" title="Colors">
Beautiful, thought-out palettes with auto dark mode.
</ListItem>
<ListItem href="https://icons.radix-ui.com/" title="Icons">
A crisp set of 15x15 icons, balanced and consistent.
</ListItem>
</ul>
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger className="NavigationMenuTrigger">
Overview <CaretDownIcon className="CaretDown" aria-hidden />
</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
<ul className="List two">
<ListItem title="Introduction" href="/primitives/docs/overview/introduction" >
Build high-quality, accessible design systems and web apps.
</ListItem>
<ListItem title="Getting started" href="/primitives/docs/overview/getting-started" >
A quick tutorial to get you up and running with Radix
Primitives.
</ListItem>
<ListItem title="Styling" href="/primitives/docs/guides/styling">
Unstyled and compatible with any styling solution.
</ListItem>
<ListItem title="Animation" href="/primitives/docs/guides/animation" >
Use CSS keyframes or any animation library of your choice.
</ListItem>
<ListItem title="Accessibility" href="/primitives/docs/overview/accessibility" >
Tested in a range of browsers and assistive technologies.
</ListItem>
<ListItem title="Releases" href="/primitives/docs/overview/releases" >
Radix Primitives releases and their changelogs.
</ListItem>
</ul>
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Link className="NavigationMenuLink" href="https://github.com/radix-ui" >
Github
</NavigationMenu.Link>
</NavigationMenu.Item>
<NavigationMenu.Indicator className="NavigationMenuIndicator">
<div className="Arrow" />
</NavigationMenu.Indicator>
</NavigationMenu.List>
<div className="ViewportPosition">
<NavigationMenu.Viewport className="NavigationMenuViewport" />
</div>
</NavigationMenu.Root>
);
};
const ListItem = React.forwardRef(
({ className, children, title, ...props }, forwardedRef) => (
<li>
<NavigationMenu.Link asChild>
<a className={classNames("ListItemLink", className)} {...props} ref={forwardedRef} >
<div className="ListItemHeading">{title}</div>
<p className="ListItemText">{children}</p>
</a>
</NavigationMenu.Link>
</li>
),
);
export default NavigationMenuDemo;

特性

    可以是受控的或非受控的。

    灵活的布局结构,带有受管理的标签焦点。

    支持子菜单。

    可选的活动项指示器。

    完整的键盘导航。

    公开 CSS 变量以实现高级动画。

    支持自定义时序。

安装

从命令行安装组件。

npm install @radix-ui/react-navigation-menu

结构

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

import { NavigationMenu } from "radix-ui";
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger />
<NavigationMenu.Content>
<NavigationMenu.Link />
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Link />
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger />
<NavigationMenu.Content>
<NavigationMenu.Sub>
<NavigationMenu.List />
<NavigationMenu.Viewport />
</NavigationMenu.Sub>
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Indicator />
</NavigationMenu.List>
<NavigationMenu.Viewport />
</NavigationMenu.Root>
);

API 参考

根组件

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

属性类型默认值
defaultValue
字符串
无默认值
value
字符串
无默认值
onValueChange
函数
无默认值
delayDuration
数字
200
skipDelayDuration
数字
300
dir
枚举
无默认值
orientation
枚举
"horizontal"
数据属性
[data-orientation]"vertical" |"horizontal"

Sub

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

属性类型默认值
defaultValue
字符串
无默认值
value
字符串
无默认值
onValueChange
函数
无默认值
orientation
枚举
"horizontal"
数据属性
[data-orientation]"vertical" |"horizontal"

List

包含顶级菜单项。

属性类型默认值
asChild
布尔值
false
数据属性
[data-orientation]"vertical" |"horizontal"

Item

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

属性类型默认值
asChild
布尔值
false
value
字符串
无默认值

Trigger

切换内容的按钮。

属性类型默认值
asChild
布尔值
false
数据属性
[data-state]"open" |"closed"
[data-disabled]

禁用时出现

Content

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

属性类型默认值
asChild
布尔值
false
onEscapeKeyDown
函数
无默认值
onPointerDownOutside
函数
无默认值
onFocusOutside
函数
无默认值
onInteractOutside
函数
无默认值
forceMount
布尔值
无默认值
数据属性
[data-state]"open" |"closed"
[data-motion]"to-start" |"to-end" |"from-start" |"from-end"
[data-orientation]"vertical" |"horizontal"

导航链接。

属性类型默认值
asChild
布尔值
false
active
布尔值
false
onSelect
函数
无默认值
数据属性
[data-active]

激活时出现

Indicator

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

属性类型默认值
asChild
布尔值
false
forceMount
布尔值
无默认值
数据属性
[data-state]"visible" |"hidden"
[data-orientation]"vertical" |"horizontal"

Viewport

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

属性类型默认值
asChild
布尔值
false
forceMount
布尔值
无默认值
数据属性
[data-state]"open" |"closed"
[data-orientation]"vertical" |"horizontal"
CSS 变量描述
--radix-navigation-menu-viewport-width视口在可见/隐藏时的宽度,从活动内容计算得出
--radix-navigation-menu-viewport-height视口在可见/隐藏时的高度,从活动内容计算得出

示例

垂直

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

<NavigationMenu.Root orientation="vertical">
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>Item Two content</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>

灵活布局

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

<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>Item two content</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
{/* NavigationMenu.Content will be rendered here when active */}
<NavigationMenu.Viewport />
</NavigationMenu.Root>

带指示器

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

// index.jsx
import { NavigationMenu } from "radix-ui";
import "./styles.css";
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>Item two content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Indicator className="NavigationMenuIndicator" />
</NavigationMenu.List>
<NavigationMenu.Viewport />
</NavigationMenu.Root>
);
/* styles.css */
.NavigationMenuIndicator {
background-color: grey;
}
.NavigationMenuIndicator[data-orientation="horizontal"] {
height: 3px;
transition:
width,
transform,
250ms ease;
}

带子菜单

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

<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>
<NavigationMenu.Sub defaultValue="sub1">
<NavigationMenu.List>
<NavigationMenu.Item value="sub1">
<NavigationMenu.Trigger>Sub item one</NavigationMenu.Trigger>
<NavigationMenu.Content>
Sub item one content
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item value="sub2">
<NavigationMenu.Trigger>Sub item two</NavigationMenu.Trigger>
<NavigationMenu.Content>
Sub item two content
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Sub>
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>

使用客户端路由

如果您需要使用路由包提供的 Link 组件,那么我们建议通过自定义组件与 NavigationMenu.Link 组合使用。这将确保可访问性并保持一致的键盘控制。这是一个使用 Next.js 的示例

// index.jsx
import { usePathname } from "next/navigation";
import NextLink from "next/link";
import { NavigationMenu } from "radix-ui";
import "./styles.css";
const Link = ({ href, ...props }) => {
const pathname = usePathname();
const isActive = href === pathname;
return (
<NavigationMenu.Link asChild active={isActive}>
<NextLink href={href} className="NavigationMenuLink" {...props} />
</NavigationMenu.Link>
);
};
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<Link href="/">Home</Link>
</NavigationMenu.Item>
<NavigationMenu.Item>
<Link href="/about">About</Link>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>
);
/* styles.css */
.NavigationMenuLink {
text-decoration: none;
}
.NavigationMenuLink[data-active] {
text-decoration: "underline";
}

高级动画

我们公开了 --radix-navigation-menu-viewport-[width|height]data-motion['from-start'|'to-start'|'from-end'|'to-end'] 属性,以便您可以根据进入/退出方向来动画化 Viewport 大小和 Content 位置。

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

// index.jsx
import { NavigationMenu } from "radix-ui";
import "./styles.css";
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
Item one content
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
Item two content
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
<NavigationMenu.Viewport className="NavigationMenuViewport" />
</NavigationMenu.Root>
);
/* 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(--radix-navigation-menu-viewport-width);
height: var(--radix-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);
}
}

可访问性

遵循 navigation 角色要求

与菜单栏的区别

NavigationMenu 不应与 menubar 混淆,尽管这个基础组件在口语意义上共享名称 menu 以指代一组导航链接,但它不使用 WAI-ARIA menu 角色。这是因为 menumenubars 的行为类似于桌面应用程序窗口中最常见的原生操作系统菜单,因此它们具有复杂的功能,例如复合焦点管理和首字母导航。

这些功能通常被认为对于网站导航是不必要的,并且最坏的情况可能会使用户对已建立的网站模式感到困惑。

有关更多信息,请参阅 W3C Disclosure Navigation Menu 示例

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

键盘交互

描述
空格Enter
当焦点在 NavigationMenu.Trigger 上时,打开内容。
Tab
将焦点移动到下一个可聚焦元素。
向下箭头
horizontal 且焦点在一个打开的 NavigationMenu.Trigger 上时,将焦点移入 NavigationMenu.Content.
将焦点移动到下一个 NavigationMenu.Trigger NavigationMenu.Link.
向上箭头
将焦点移动到上一个 NavigationMenu.Trigger NavigationMenu.Link.
向右箭头向左箭头
vertical 且焦点在一个打开的 NavigationMenu.Trigger 上时,将焦点移入其 NavigationMenu.Content.
将焦点移动到下一个/上一个 NavigationMenu.TriggerNavigationMenu.Link
Home End
将焦点移动到第一个/最后一个 NavigationMenu.Trigger NavigationMenu.Link.
Esc
关闭打开的 NavigationMenu.Content 并将焦点移动到其 NavigationMenu.Trigger.
上一个Menubar
下一个Popover