Menu
Build a horizontal hover navigation bar with animated dropdown panels.
Menu is a client component that renders a horizontal navigation bar with animated dropdown panels, for site headers. Hovering or clicking a MenuItem with children selects it, and that item's children render as the dropdown panel, typically a MenuContent. MenuItem uses next/link for items with a path.
Import#
import { Menu, MenuItem, MenuContent } from "@reactberry/system/blocks";
Usage#
import { Menu, MenuItem, MenuContent } from "@reactberry/system/blocks";
const productLinks = [
{ documentId: "analytics", title: "Analytics", path: "/analytics", additionalFields: { description: "Track usage" } },
{ documentId: "billing", title: "Billing", path: "/billing" },
];
export default function SiteNav() {
return (
<Menu>
<MenuItem id={1} title="Products">
<MenuContent items={productLinks} />
</MenuItem>
<MenuItem id={2} title="Pricing" path="/pricing" />
<MenuItem id={3} title="Docs" path="/docs" />
</Menu>
);
}
Examples#
Categories#
Give an item nested items to render it as a category heading with its links below. At the top level, each item gets its own column.
Custom panel#
Pass children instead of items to render any content in the panel.
API#
Menu#
| Prop | Type | Default | Description |
|---|---|---|---|
children* | React.ReactNode | — |
|
The selection is cleared when the pointer leaves the menu. No other props are accepted.
MenuItem#
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | number | — | Unique id. Matched against the selected id, and used for the title element id |
title* | string | — | Label text. |
path | string | — | Renders the title as a Next.js |
children | React.ReactNode | — | Dropdown panel shown while this item is selected, usually a |
disabled | boolean | — | Applies the |
A pill highlight with a shared layoutId follows the hovered or selected item. It can be styled through theme.header.public.menu.item.highlight.
MenuContent#
| Prop | Type | Default | Description |
|---|---|---|---|
items | MenuItem[] | — | Link data. Items with nested |
children | React.ReactNode | — | Custom panel content, rendered when |
level | number | 0 | Nesting level. At level |
Each entry in items has this shape:
| Prop | Type | Default | Description |
|---|---|---|---|
documentId* | string | — | Unique key. |
title* | string | — | Link text, or the category heading when |
path | string | — |
|
items | MenuItem[] | — | Nested items. Turns the entry into a category heading. |
additionalFields | { description?: string; divider?: boolean } | — |
|
menuAttached | boolean | — | Declared in the type but not used. |
order | number | — | Declared in the type but not used. |
Accessibility#
- Navigation is pointer-driven. There is no keyboard handling and no ARIA menu semantics, so panels can't be opened from the keyboard.
- Titles with a
pathare links and can be focused with Tab. Titles without apathare plainspanelements. - Links in
MenuContentare nativeaelements.
Notes#
MenuItemandMenuContentmust be rendered insideMenu; they read the shared menu context and throw otherwise.- The panel is absolutely positioned below the bar (
width="40rem"), with a nub that tracks the centre of the selected title. The nub position is not updated when the selected id is0or another falsy value. - Slide direction between panels compares ids with
>, so numeric ids in display order give the expected direction. - Hovering a title that has a
pathclears the selection, so combiningpathandchildrenon one item does not keep its panel open. disabledsetspointer-events: noneon the item, so it can't be hovered or clicked, but apathlink can still be reached with the keyboard.MenuContentsetsid="overlay-content"on its panel; only one panel is rendered at a time.