Components

导航菜单

用于浏览网站的链接集合。

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;

Features

    可以是受控或非受控的。

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

    支持子菜单。

    可选的活动项指示器。

    全面的键盘导航。

    暴露用于高级动画的 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 参考

Root

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

PropTypeDefault
defaultValue
string
No default value
value
string
No default value
onValueChange
function
No default value
delayDuration
number
200
skipDelayDuration
number
300
dir
enum
No default value
orientation
enum
"horizontal"
Data attributeValues
[data-orientation]"vertical" | "horizontal"

Sub

表示子菜单。在嵌套时要用它替代根部分以创建子菜单。

PropTypeDefault
defaultValue
string
No default value
value
string
No default value
onValueChange
function
No default value
orientation
enum
"horizontal"
Data attributeValues
[data-orientation]"vertical" | "horizontal"

List

包含顶级菜单项。

PropTypeDefault
asChild
boolean
false
Data attributeValues
[data-orientation]"vertical" | "horizontal"

Item

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

PropTypeDefault
asChild
boolean
false
value
string
No default value

Trigger

切换内容的按钮。

PropTypeDefault
asChild
boolean
false
Data attributeValues
[data-state]"open" | "closed"
[data-disabled]

当禁用时显示

Content

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

PropTypeDefault
asChild
boolean
false
onEscapeKeyDown
function
No default value
onPointerDownOutside
function
No default value
onFocusOutside
function
No default value
onInteractOutside
function
No default value
forceMount
boolean
No default value
Data attributeValues
[data-state]"open" | "closed"
[data-motion]"to-start" | "to-end" | "from-start" | "from-end"
[data-orientation]"vertical" | "horizontal"

一个导航链接。

PropTypeDefault
asChild
boolean
false
active
boolean
false
onSelect
function
No default value
Data attributeValues
[data-active]

当活动时显示

Indicator

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

PropTypeDefault
asChild
boolean
false
forceMount
boolean
No default value
Data attributeValues
[data-state]"visible" | "hidden"
[data-orientation]"vertical" | "horizontal"

Viewport

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

PropTypeDefault
asChild
boolean
false
forceMount
boolean
No default value
Data attributeValues
[data-state]"open" | "closed"
[data-orientation]"vertical" | "horizontal"
CSS VariableDescription
--radix-navigation-menu-viewport-width视口在可见/隐藏状态下的宽度,从活动内容计算
--radix-navigation-menu-viewport-height视口在可见/隐藏状态下的高度,从活动内容计算

示例

垂直

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

<NavigationMenu.Root orientation="vertical">
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目一</NavigationMenu.Trigger>
<NavigationMenu.Content>项目一内容</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目二</NavigationMenu.Trigger>
<NavigationMenu.Content>项目二内容</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>

灵活布局

当你需要额外控制 Content 的渲染位置时,使用 Viewport 部分。当你的设计需要调整的 DOM 结构,或需要灵活性以实现 高级动画 时,这非常有用。 标签焦点将自动保持。

<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目一</NavigationMenu.Trigger>
<NavigationMenu.Content>项目一内容</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目二</NavigationMenu.Trigger>
<NavigationMenu.Content>项目二内容</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
{/* NavigationalMenu.Content激活时将在这里渲染 */}
<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>项目一</NavigationMenu.Trigger>
<NavigationMenu.Content>项目一内容</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目二</NavigationMenu.Trigger>
<NavigationMenu.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>项目一</NavigationMenu.Trigger>
<NavigationMenu.Content>项目一内容</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目二</NavigationMenu.Trigger>
<NavigationMenu.Content>
<NavigationMenu.Sub defaultValue="sub1">
<NavigationMenu.List>
<NavigationMenu.Item value="sub1">
<NavigationMenu.Trigger>子项目一</NavigationMenu.Trigger>
<NavigationMenu.Content>
子项目一内容
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item value="sub2">
<NavigationMenu.Trigger>子项目二</NavigationMenu.Trigger>
<NavigationMenu.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="/">首页</Link>
</NavigationMenu.Item>
<NavigationMenu.Item>
<Link href="/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>项目一</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
项目一内容
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>项目二</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
项目二内容
</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 示例以获取更多信息。

链接使用和 aria-current

在菜单中使用 NavigationMenu.Link 进行所有导航链接很重要,这不仅适用于主列表,也适用于通过 NavigationMenu.Content 渲染的任何内容。这将确保一致的键盘交互和无障碍性,同时也提供了 active 属性以设置 aria-current 和活动样式。 有关与第三方路由组件一起使用的更多信息,请参见 此示例

键盘交互

KeyDescription
SpaceEnter
当焦点在 NavigationMenu.Trigger 上时,打开内容。
Tab
将焦点移动到下一个可聚焦元素。
ArrowDown
horizontal 并且焦点在打开的 NavigationMenu.Trigger 上时,将焦点移入 NavigationMenu.Content
将焦点移到下一个 NavigationMenu.TriggerNavigationMenu.Link
ArrowUp
将焦点移到上一个 NavigationMenu.TriggerNavigationMenu.Link
ArrowRightArrowLeft
vertical 并且焦点在打开的 NavigationMenu.Trigger 上时,将焦点移入其 NavigationMenu.Content
将焦点移到下一个/上一个 NavigationMenu.TriggerNavigationMenu.Link
HomeEnd
将焦点移到第一个/最后一个 NavigationMenu.TriggerNavigationMenu.Link
Esc
关闭打开的 NavigationMenu.Content 并将焦点移到其 NavigationMenu.Trigger