Navigation Menu
Overview
Use the Navigation Menu family when you need authored navigation items with optional dropdown panels, keyboard navigation, animated viewport transitions, and a mobile drawer helper that automatically derives its structure from the desktop menu. The desktop family is composed of six components that nest together, plus a separate mobile helper and an explicit mobile-only node for complex mega-menu content.
The root component manages open state, keyboard navigation, and optional animated viewport behavior. Navigation Menu Mobile is a separate component placed elsewhere on the page — it connects to the desktop menu by a shared menuId and builds a drawer-based mobile experience at runtime.
Authoring Structure
The desktop menu follows a strict nesting hierarchy:
Navigation Menu (desktop root)
└── Navigation Menu List
├── Navigation Menu Item
│ ├── plain link
│ ├── OR Navigation Menu Trigger + Navigation Menu Content
│ │ └── optional Navigation Menu Mobile Item subtree inside content
│ └── OR link + Navigation Menu Trigger + Navigation Menu Content
└── Navigation Menu Item ...
Navigation Menu Mobile (separate — anywhere on the page)
Desktop and Mobile Are Separate
Navigation Menu Mobile is not nested inside Navigation Menu. It is placed as a sibling element elsewhere on the page (typically inside the header shell alongside the desktop navigation). Both components share the same menuId value so the mobile helper can find and mirror the desktop structure at runtime.
In header patterns, the typical structure is:
Header Shell
├── Brand
├── Navigation Menu (desktop, menuId="primary-nav")
│ └── Navigation Menu List ...
├── Actions
└── Navigation Menu Mobile (separate, menuId="primary-nav")
Placement Rules
| Component | Placement | Role |
|---|---|---|
| Navigation Menu | Top-level wrapper for the desktop navigation. | Owns orientation, open-state coordination, viewport behavior, and the menu identity. |
| Navigation Menu List | Direct child of Navigation Menu. | Groups authored top-level menu items. The runtime scans for this list when initializing. |
| Navigation Menu Item | Direct child of Navigation Menu List. | One navigation entry — a plain link, a dropdown parent, or both. |
| Navigation Menu Trigger | Inside Navigation Menu Item. | Interactive button that opens a dropdown panel. |
| Navigation Menu Content | Inside Navigation Menu Item, alongside a trigger. | Dropdown or mega-menu panel content. |
| Navigation Menu Mobile Item | Inside Navigation Menu Content. | Declares an explicit mobile tree for complex mega-menu content. |
| Navigation Menu Mobile | Separate from the desktop menu — anywhere on the page, connected by menuId. | Mirrors the desktop menu into a drawer-based mobile experience at runtime. |
Quick Start
<OmeNavigationMenu identity='{{"menuId":"primary-nav"}}'>
{#slot default}
<OmeNavigationMenuList>
{#slot default}
<OmeNavigationMenuItem>
{#slot default}
<OmeNavigationMenuTrigger settings='{{"showArrow":true,"triggerAction":"click"}}'>
{#slot default}Products{/slot}
</OmeNavigationMenuTrigger>
<OmeNavigationMenuContent>
{#slot default}
<a href="/shop">Shop all products</a>
{/slot}
</OmeNavigationMenuContent>
{/slot}
</OmeNavigationMenuItem>
<OmeNavigationMenuItem>
{#slot default}
<a href="/about">About</a>
{/slot}
</OmeNavigationMenuItem>
{/slot}
</OmeNavigationMenuList>
{/slot}
</OmeNavigationMenu>
<!-- Desktop menu -->
<OmeNavigationMenu
identity='{{"menuId":"primary-nav"}}'
settings='{{"useAnimatedMenu":true,"useFullWidth":true,"fullWidthTargetSelector":".site-shell"}}'
>
{#slot default}
<OmeNavigationMenuList>
{#slot default}
<OmeNavigationMenuItem>
{#slot default}
<OmeNavigationMenuTrigger settings='{{"showArrow":true,"triggerAction":"click"}}'>
{#slot default}Services{/slot}
</OmeNavigationMenuTrigger>
<OmeNavigationMenuContent>
{#slot default}
<a href="/services/design">Design</a>
<a href="/services/dev">Development</a>
{/slot}
</OmeNavigationMenuContent>
{/slot}
</OmeNavigationMenuItem>
{/slot}
</OmeNavigationMenuList>
{/slot}
</OmeNavigationMenu>
<!-- Mobile helper (separate, linked by menuId) -->
<OmeNavigationMenuMobile
identity='{{"menuId":"primary-nav"}}'
content='{{"triggerLabel":"Open menu"}}'
/>
The mobile helper reads the desktop menu structure at runtime and generates a drawer-based mobile navigation. No duplicate authoring is needed.
Family Components
Navigation Menu (Root)
The root component wraps all desktop navigation items and manages shared behavior: orientation, keyboard navigation, animated viewport mode, and the menu identity used by the mobile helper. It does not render significant visible UI itself — only a container element and an optional viewport.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | nav | HTML tag for the root element. Defaults to |
Identity Props
| Prop | Type | Default | Description |
|---|---|---|---|
menuId | string | "" | Shared menu ID that |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | horizontal | Root orientation. Affects which arrow keys navigate between triggers and the layout direction. Use |
useAnimatedMenu | boolean | false | Enables the generated viewport that animates dropdown panel changes. When enabled, content panels are portaled into a viewport container with smooth height transitions and directional motion. |
useFullWidth | boolean | false | Makes the animated viewport align to a wider container instead of the trigger width. Requires |
fullWidthTargetSelector | string | "" | CSS selector used to find the container element whose width the viewport should match. Only visible when |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-nav-menu-default | CSS class applied to the root element. |
viewportClass | class | ome-nav-menu-default__viewport | CSS class applied to the generated animated viewport container. Only visible when |
Animated Viewport Mode
When useAnimatedMenu is true, the root component generates a viewport container that houses all dropdown panels. This enables:
- Smooth height transitions as panels open and close.
- Directional motion animations when switching between panels (content slides in from the direction of the newly focused trigger).
- Full-width mode that aligns the viewport to a wider parent container for mega-menu layouts.
Animated mode only activates for horizontal orientation and is automatically disabled inside the Etch builder (where it falls back to a non-animated preview).
Keyboard Behavior
When a trigger has focus in a horizontal menu:
| Key | Action |
|---|---|
Enter / Space | Toggle the associated panel. |
ArrowRight | Move focus to the next trigger. |
ArrowLeft | Move focus to the previous trigger. |
ArrowDown | Open the dropdown panel. |
Home | Move focus to the first trigger. |
End | Move focus to the last trigger. |
Escape | Close the open panel and return focus to its trigger. |
In a vertical menu, ArrowDown / ArrowUp navigate between triggers and ArrowRight opens the panel.
Navigation wraps by default — pressing ArrowRight on the last trigger focuses the first, and vice versa.
Accessibility
The runtime wires up ARIA attributes automatically:
- Each trigger has
aria-expandedandaria-haspopup="menu"that update as panels open and close. - Each content panel has
role="menu"and syncsaria-hiddenwith its open state. data-ome-stateattributes (open/closed) are set on items, triggers, and content for CSS targeting.
Navigation Menu List
The list container groups top-level menu items. The runtime scans for the direct Navigation Menu List child when initializing the root component.
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-nav-menu-default__list | CSS class applied to the list element. |
The list always renders as a <ul> element with display: flex and reset list styles. In horizontal mode the flex direction is row; in vertical mode it becomes column.
Navigation Menu Item
Each NavigationMenuItem represents one navigation entry. It can contain a plain link, a trigger-plus-content dropdown pair, or a combination of both (a link that also opens a dropdown).
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Disables this item and any trigger it contains. Disabled items cannot be opened or focused for activation. |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-nav-menu-default__item | CSS class applied to the item element. |
The item always renders as an <li> element. The runtime tracks open/closed state via data-ome-state on the item.
Navigation Menu Trigger
The interactive button users click or hover to open a dropdown panel. The trigger always renders as a <button> element and can optionally display a built-in chevron icon.
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Prevents this trigger from opening its content panel. Overrides the item-level disabled prop. |
showArrow | boolean | false | Shows a built-in chevron icon inside the trigger button. The chevron rotates 180 degrees when the panel is open. |
triggerAction | "hover" | "click" | hover | Preferred interaction mode. |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-nav-menu-default__trigger | CSS class applied to the trigger button. |
Trigger Interaction Modes
hover— The panel opens when the cursor enters the trigger area (with a 50ms delay) and closes when the cursor leaves the entire navigation boundary (trigger + content + viewport). This is the default and provides the smoothest desktop experience.click— The panel only opens on explicit click. A document-level click listener closes the panel when clicking outside the navigation root.
Both modes support keyboard activation via Enter / Space.
Navigation Menu Content
The dropdown or mega-menu panel paired with a trigger. Content panels are hidden by default and become visible when their sibling trigger opens them.
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-nav-menu-default__content | CSS class applied to the content panel. |
Runtime Behavior
- In non-animated mode, content is positioned absolutely below its trigger item (or to the right in vertical orientation).
- In animated mode, content is portaled into the shared viewport container and animated with presence state transitions.
- Content panels use
role="menu",aria-hidden, anddata-ome-statethat sync betweenopenandclosed. - The
hiddenattribute is toggled to control visibility.
Navigation Menu Mobile
A separate component that mirrors the desktop navigation into a drawer-based mobile experience. It is not nested inside Navigation Menu — it is placed elsewhere on the page and connects to the desktop menu via a shared menuId.
At runtime, the mobile helper:
- Finds the desktop
Navigation Menuroot by matchingmenuId. - Extracts the menu structure recursively (or reads explicit
Navigation Menu Mobile Itemnodes if present). - Generates a panel-based mobile drawer with sliding navigation and a back button.
In the Etch builder, the helper shows a static preview instead of live-extracted mobile navigation.
Identity Props
| Prop | Type | Default | Description |
|---|---|---|---|
menuId | string | ome-mobile-nav | Must match |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
drawerDirection | "left" | "right" | "top" | "bottom" | left | Direction the mobile drawer slides from. |
dismissible | boolean | true | Allows overlay click and |
Content Props
| Prop | Type | Default | Description |
|---|---|---|---|
showTriggerLabel | boolean | true | When enabled, the trigger displays visible label text. When disabled, only a hamburger icon is shown with a separate |
triggerLabel | string | Menu | Visible label text and accessible name for the drawer trigger. Only shown when |
triggerAriaLabel | string | Menu | Screen-reader-only label for the trigger button when |
backButtonLabel | string | Back | Fallback label used by the back button when the |
Root Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-mobile-nav-default | CSS class applied to the mobile helper wrapper. |
Control Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
triggerClass | class | ome-mobile-nav-default__trigger | CSS class applied to the drawer trigger button. |
backButtonClass | class | ome-mobile-nav-default__back | CSS class applied to the back button. |
closeButtonClass | class | ome-mobile-nav-default__close | CSS class applied to the icon-only close button. |
Drawer Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
drawerClass | class | ome-mobile-nav-default__drawer | CSS class applied to the internal drawer root. |
headerClass | class | ome-mobile-nav-default__header | CSS class applied to the drawer header row. |
contentClass | class | ome-mobile-nav-default__content | CSS class applied to the drawer content wrapper. |
Menu Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
listClass | class | ome-mobile-nav-default__list | CSS class applied to generated root and child lists. |
itemClass | class | ome-mobile-nav-default__item | CSS class applied to generated list items. |
linkClass | class | ome-mobile-nav-default__link | CSS class applied to generated links. |
submenuTriggerClass | class | ome-mobile-nav-default__submenu-trigger | CSS class applied to generated submenu trigger buttons. |
nextIconClass | class | ome-mobile-nav-default__next-icon | CSS class applied to the chevron icon shown on generated submenu triggers. |
Slot Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
beforeClass | class | ome-mobile-nav-default__before | CSS class applied to the wrapper around the |
afterClass | class | ome-mobile-nav-default__after | CSS class applied to the wrapper around the |
Slots
| Slot | Description |
|---|---|
before | Renders before generated items in the root panel only. |
after | Renders after generated items in the root panel only. |
backButton | Replaces the default back button content. If omitted, the button falls back to a back icon plus backButtonLabel. |
Mobile Keyboard Behavior
Inside the mobile drawer, when a menu item has focus:
| Key | Action |
|---|---|
ArrowDown | Move focus to the next menu item. |
ArrowUp | Move focus to the previous menu item. |
Home | Move focus to the first menu item. |
End | Move focus to the last menu item. |
Enter / Space | Activate the focused item (follow link or open submenu). |
ArrowRight | Open a submenu (when focused on a submenu trigger). |
Escape / ArrowLeft | Go back to the previous panel. |
How Content Extraction Works
The mobile helper extracts the navigation tree from the desktop menu in one of two ways:
- Explicit mobile items — If a
Navigation Menu Contentsubtree contains at least oneNavigation Menu Mobile Item, the helper uses only those explicit nodes for that panel and ignores regular auto-generated links. - Automatic extraction — If no explicit mobile items exist, the helper recursively extracts the authored structure: links become mobile links, items with triggers become submenu branches, and nested navigation roots are traversed.
Navigation Menu Mobile Item
An explicit mobile-only node authored inside Navigation Menu Content when complex mega-menu content needs a custom mobile tree that differs from the auto-extracted structure. If a content panel contains at least one Navigation Menu Mobile Item, the mobile helper uses only those explicit nodes.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | div | HTML tag for the authored wrapper element. Common values are |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
useAs | "trigger" | "link" | trigger | Mobile node mode. |
Content Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "" | Label shown in the generated mobile drawer UI for this node. |
Link Props
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | "" | Destination URL. Required when |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | [] | Optional CSS class list for the authored wrapper element. |
When to Use Mobile Items
Use Navigation Menu Mobile Item when your desktop mega-menu has complex content (cards, images, multi-column layouts) that should not be naively extracted into the mobile drawer. By wrapping mobile-relevant content in explicit mobile items, you control exactly what appears in the mobile navigation while keeping the desktop layout free to include richer visual content.
Common Mistakes
Do not place Navigation Menu Item blocks directly inside Navigation Menu. The runtime scans for a direct Navigation Menu List child at initialization. Always nest items inside a list.
Navigation Menu Mobile is a separate component. It must not be placed inside the desktop Navigation Menu. Place it elsewhere on the page and connect it via menuId.
If an item has Navigation Menu Content, it must also have a sibling Navigation Menu Trigger. Content without a trigger will not be openable.
The menuId on Navigation Menu Mobile.identity.menuId must exactly match Navigation Menu.identity.menuId. If the IDs do not match, the mobile helper will not find the desktop menu and the drawer will be empty.
Do not author a separate second menu tree for mobile. Navigation Menu Mobile derives its structure from the desktop menu automatically. Only use Navigation Menu Mobile Item when you need to customize the mobile tree for complex content.
FAQs
Can I use Navigation Menu without the mobile helper?
Yes. Navigation Menu Mobile is entirely optional. The desktop navigation family works standalone. Only add the mobile helper when you need a drawer-based mobile experience that mirrors the desktop menu.
How does the animated viewport work?
When useAnimatedMenu is enabled on the root component, dropdown content panels are portaled into a shared viewport container. The viewport smoothly transitions its height when panels open and close, and content panels animate with directional motion (slide in from the direction of the newly focused trigger). For full-width mega-menus, enable useFullWidth and set fullWidthTargetSelector to the container the viewport should align to.
What happens when both a link and a trigger are in the same item?
The item renders as a "link-branch" — the link is clickable for navigation and the trigger opens a dropdown. In the mobile drawer, this becomes a row with both a tappable link and a submenu chevron button.
Can I nest navigation menus?
Yes. You can place a Navigation Menu inside a Navigation Menu Content panel for multi-level navigation. Nested menus manage their own state independently. The animated viewport is automatically disabled for nested menus to avoid conflicts.
How do I customize the mobile drawer trigger?
Use showTriggerLabel to toggle between a labeled trigger and an icon-only trigger. When showTriggerLabel is false, set triggerAriaLabel for screen reader accessibility. Style the trigger using controlStyling.triggerClass. For the hamburger icon itself, the component renders a built-in SVG.
When should I use Navigation Menu Mobile Item?
Use it when your desktop mega-menu has complex visual content (cards, multi-column layouts, featured sections) that should not be auto-extracted into the mobile drawer. By wrapping mobile-relevant content in explicit Navigation Menu Mobile Item nodes, you control exactly what appears in the mobile navigation. Simple dropdown menus do not need explicit mobile items — the auto-extraction handles them well.