组件

导航菜单

用于网站导航的一组链接。

import React from 'react';
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
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 * as NavigationMenu from '@radix-ui/react-navigation-menu';
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"

表示子菜单。嵌套时,将其替换为根部分以创建子菜单。

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

列表

包含顶级菜单项。

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

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

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

触发器

切换内容的按钮。

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

禁用时出现

内容

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

属性类型默认值
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]

处于活动状态时出现

指示器

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

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

视口

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

属性类型默认值
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 * as NavigationMenu from '@radix-ui/react-navigation-menu';
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 * as NavigationMenu from '@radix-ui/react-navigation-menu';
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 * as NavigationMenu from '@radix-ui/react-navigation-menu';
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 和活动样式。请参阅 此示例,以获取有关与第三方路由组件一起使用的更多信息。

键盘交互

描述
空格回车
当焦点位于 NavigationMenu.Trigger 上时,打开内容。
Tab
将焦点移动到下一个可聚焦元素。
向下箭头
horizontal 且焦点位于打开的 NavigationMenu.Trigger 上时,将焦点移动到 NavigationMenu.Content.
将焦点移动到下一个 NavigationMenu.Trigger NavigationMenu.Link.
向上箭头
将焦点移动到上一个 NavigationMenu.Trigger NavigationMenu.Link.
向右箭头向左箭头
vertical 且焦点位于打开的 NavigationMenu.Trigger 上时,将焦点移动到其 NavigationMenu.Content.
将焦点移动到下一个/上一个 NavigationMenu.TriggerNavigationMenu.Link
HomeEnd
将焦点移动到第一个/最后一个 NavigationMenu.Trigger NavigationMenu.Link.
Esc
关闭打开的 NavigationMenu.Content 并将焦点移动到其 NavigationMenu.Trigger.
上一个菜单栏
下一个弹出框