The navigation menu component provides a horizontal or vertical navigation system for web applications. It supports both simple navigation links and complex dropdown content areas with unlimited nesting depth, making it ideal for main navigation bars, mega menus, and hierarchical navigation structures. The component features intelligent hover behavior, keyboard navigation, and automatic active state management.
Mega menus are preferred over multi-level nested menus for better user experience. Mega menus provide all options at once, reducing navigation depth and making it easier for users to find what they're looking for. Use multi-level nesting only when absolutely necessary for complex hierarchical structures.
✅ Use navigation menu components when:
- Creating main site navigation bars
- Implementing mega menus with rich content
- Organizing hierarchical navigation structures with multiple levels
- Building vertical side navigation menus
- Creating navigation with hover-activated dropdowns
❌ Don't use navigation menu components for:
- Simple action menus (use command components)
- Form selection dropdowns (use select or combobox)
- Contextual actions on content (use command components)
- Breadcrumb navigation (use breadcrumb component)
A simple horizontal navigation bar with active state management and clean styling.
Navigation items can contain rich dropdown content for complex menu structures.
Create complex mega menus with grid layouts and organized content sections.
Navigation menus support unlimited nesting depth with automatic placement and keyboard navigation.
Navigation items support custom trigger elements beyond standard buttons.
Customize the expand indicator icon using the indicator slot.
Implementation of a complete site navigation with branding and user actions.
The navigation menu component can be customized using CSS custom properties for layout spacing and popup appearance.
.custom-navigation {
/* Increase spacing between navigation items */
--dds-navigation-menu-item-gap: var(--dds-spacing-300);
}
.custom-navigation-item {
/* Customize dropdown appearance */
--dds-popup-background: var(--dds-background-neutral-subtle);
--dds-popup-border-radius: var(--dds-radius-large);
--dds-popup-border-color: red;
--dds-popup-border-width: 2px;
--dds-popup-padding: var(--dds-spacing-400);
}
.custom-navigation-item dap-ds-button[variant="subtle-menu"] {
/* Custom button styling for menu items */
--dds-button-padding-x: var(--dds-spacing-400);
--dds-button-padding-y: var(--dds-spacing-300);
--dds-button-border-radius: var(--dds-radius-large);
/* Hover and active states */
--dds-button-subtle-background-neutral-hover: var(--dds-indigo-600);
--dds-button-subtle-background-neutral-pressed: var(--dds-indigo-700);
--dds-button-subtle-text-neutral-hover: var(--dds-button-primary-text-enabled);
}
.custom-navigation-item dap-ds-button[variant="subtle-menu-item"] {
/* Custom styling for dropdown menu items */
--dds-button-subtle-menu-item-padding: var(--dds-spacing-300);
--dds-button-subtle-menu-item-border-radius: var(--dds-radius-base);
--dds-button-subtle-menu-item-color-bg-hover: var(--dds-indigo-600);
}
- Full keyboard navigation with Arrow keys, Tab, Enter, and Escape
- Screen reader support with proper ARIA roles and labels
- Focus management with automatic dropdown closure
- Active state announcements for current page/section
- Tab: Navigate between top-level items
- ArrowLeft/ArrowRight (horizontal): Navigate between items
- ArrowUp/ArrowDown (vertical): Navigate between items
- ArrowDown/ArrowUp (horizontal): Open submenu if item has one
- ArrowRight (vertical): Open submenu if item has one
- Enter/Space: Activate current item (open submenu or navigate)
- Home/End: Jump to first/last item
- ArrowDown: Move to next sibling
- ArrowUp: Move to previous sibling
- ArrowRight: Open nested submenu or move into submenu
- ArrowLeft: Close parent submenu and return focus
- Escape: Close parent submenu
- Enter/Space: Activate link or open submenu
The component automatically provides:
- Semantic
<nav>element (no menu/menubar roles - this is navigation, not application menu) aria-current="page"for active navigation itemsaria-expandedstates for dropdown triggersaria-haspopupfor items with submenusaria-controlslinking triggers to their dropdowns- Screen readers announce as navigation landmark
- Mouse Hover: When hovering over a navigation item with a submenu, a 100ms delay timer starts. If the mouse stays, the submenu opens automatically.
- Sibling Management: When hovering over a different item, sibling items' submenus are automatically closed to ensure only one submenu is open at a time.
- Click After Hover: If a submenu was opened by hover and you click it, the submenu stays open (doesn't toggle closed). The hover flag is cleared so future clicks will toggle normally.
- Simple Links: Clicking navigates to the href
- Items with Submenus:
- First click opens the submenu
- Second click closes it (toggle behavior)
- If opened by hover first, clicking keeps it open
Items can be marked as active using different matching strategies:
- Exact Match:
href === activeHref(default) - Prefix Match: Use
baseHrefproperty for prefix matching - item will be active ifactiveHref.startsWith(baseHref) - Exact Only: Set
exactHref="true"to require exact match even whenbaseHrefis set
<!-- Prefix matching example -->
<dap-ds-navigation-menu activeHref="/products/web-dev">
<dap-ds-navigation-menu-item baseHref="/products">
<!-- This item will be active because /products/web-dev starts with /products -->
<span slot="title">Products</span>
</dap-ds-navigation-menu-item>
</dap-ds-navigation-menu>
The navigation menu component is built on top of the popup component, combining navigation semantics with positioning capabilities. It automatically manages:
- Active states based on href matching
- Dropdown interactions with hover delay and click behavior
- Keyboard navigation with full arrow key support
- Nested item management with automatic level and placement calculation
- Sibling coordination to ensure proper submenu behavior
- Positioning that escapes constrained containers using fixed positioning strategy
The component uses Floating UI for intelligent positioning and automatically adjusts placement based on nesting level and orientation.
import { DapDSNavigationMenu } from 'dap-design-system'
import { DapDSNavigationMenuReact } from 'dap-design-system/react'
For optimal bundle sizes, use the tree-shakeable import syntax:
import { DapDSNavigationMenu } from 'dap-design-system/components'
| Property | Type | Default | Description |
|---|---|---|---|
activeHref | string | The currently active href for highlighting active navigation items with aria-current="page". Defaults to window.location.pathname if not provided. | |
orientation | 'horizontal', 'vertical' | 'horizontal' | The orientation of the navigation menu. Default is 'horizontal'. |
fullWidth | boolean | false | Whether the navigation menu should take full width of the screen. Default is false. |
allowMultipleOpen | boolean | true | Whether multiple dropdown branches can stay open simultaneously (accordion mode). Default is true. |
| Name | Description |
|---|---|
(default) | The navigation menu list and items. |
| Event Name | Description | Type |
|---|---|---|
dds-navigation-item-click | Fired when a navigation item is clicked. | {href: string, event: Event } |
| Part Name | Description |
|---|---|
base | The main navigation menu container (nav element). |
You can style CSS parts using the ::part() pseudo-element selector:
/* Target a specific part */
.my-custom-dap-ds-navigation-menu::part(base) {
/* Your custom styles */
}
Example usage:
<dap-ds-navigation-menu class="my-custom-dap-ds-navigation-menu">
Navigation menu
</dap-ds-navigation-menu>
.my-custom-dap-ds-navigation-menu::part(base) {
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
CSS parts allow you to style internal elements of the component while maintaining encapsulation. Learn more in our styling guide.
| Property Name | Description |
|---|---|
--dds-navigation-menu-item-gap | The gap between navigation menu items. (default: var(--dds-spacing-100)). |
CSS custom properties (CSS variables) can be set directly on the component or in your stylesheet:
Method 1: Inline styles (Quick customization)
<dap-ds-navigation-menu
style="--dds-navigation-menu-item-gap: value; ">
Navigation menu
</dap-ds-navigation-menu>
Method 2: CSS classes (Reusable styles)
.my-custom-dap-ds-navigation-menu {
--dds-navigation-menu-item-gap: value;
}
<dap-ds-navigation-menu class="my-custom-dap-ds-navigation-menu">
Navigation menu
</dap-ds-navigation-menu>
Method 3: Global theme customization
/* Apply to all instances */
dap-ds-navigation-menu {
--dds-navigation-menu-item-gap: value;
}
CSS custom properties inherit through the Shadow DOM, making them perfect for theming. Changes apply immediately without rebuilding.
| Property | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | The disabled state of the popup. Default is false. |
opened | boolean | false | The open state of the popup. Default is false. |
placement | 'top', 'right' , 'bottom' , 'left' , 'top-start' , 'top-end' , 'bottom-start' , 'bottom-end' , 'left-start' , 'left-end' , 'right-start' , 'right-end' | 'bottom-start' | The placement of the popup (automatically adjusted based on nesting level). Default is 'bottom-start'. |
floatingStrategy | 'absolute', 'fixed' | 'fixed' | The floating strategy of the popup. Default is 'fixed'. |
offset | number | 0 | The offset of the popup. Default is 0. |
sync | boolean | false | Whether the popup should sync its width with the trigger. Default is false. |
maxHeight | number, 'auto' | 250 | The maximum height of the popup. Default is 250. |
maxWidth | number, 'auto' | 'auto' | The maximum width of the popup. Default is 'auto'. |
hasArrow | boolean | false | Whether the popup has an arrow. Default is false. |
overflow | boolean | true | Whether the popup should overflow. Default is true. |
fullWidth | boolean | false | Whether the popup should take full width of the screen. Default is false. |
icon | string | The name of the icon to display in the trigger. | |
ariaLabelledBy | string | The name of the element that labels the navigation dropdown. | |
activeHref | string | The href of the navigation item that is active (receives aria-current="page"). | |
baseHref | string | The href of the navigation item. If provided, the navigation item will be active if the href is a substring of the activeHref. | |
exactHref | boolean | false | Whether the navigation item should be active if the href is exactly the same as the activeHref. Default is false. |
level | number | 0 | The nesting level of this navigation item (0 = top-level, automatically set for nested items). Default is 0. |
orientation | 'horizontal', 'vertical' | 'horizontal' | The orientation of the parent navigation menu (automatically inherited). Default is 'horizontal'. |
hasContent | boolean | false | |
size | 'xs', 'sm' , 'lg' | The size of the popup. Default is 'sm'. |
| Name | Description |
|---|---|
trigger | The trigger element (link, button, etc.) for this navigation item. |
title | The title of the navigation item. |
indicator | The expand indicator icon (defaults to arrow icon based on orientation and level). |
(default) | The dropdown content (can contain nested dap-ds-navigation-menu-item elements). |
| Event Name | Description | Type |
|---|---|---|
dds-navigation-item-click | Fired when a navigation item is clicked. | {href: string, event: Event } |
dds-navigation-dropdown-open | Fired when a navigation dropdown is opened. | {item: DapDSNavigationMenuItem } |
| Part Name | Description |
|---|---|
base | The base part of the navigation item. |
trigger | The trigger element part. |
content | The dropdown content part. |
indicator-icon | The expand indicator icon part. |
| Property Name | Description |
|---|---|
--dds-navigation-menu-item-spacing | The padding/spacing of the navigation menu item content. (default: var(--dds-spacing-200)). |
--dds-navigation-menu-item-border-width | The border width of the navigation menu item content. (default: var(--dds-border-width-base)). |
--dds-navigation-menu-item-border-color | The border color of the navigation menu item content. (default: var(--dds-border-neutral-subtle)). |
--dds-navigation-menu-item-border-radius | The border radius of the navigation menu item content. (default: var(--dds-radius-base)). |
--dds-navigation-menu-item-background | The background color of the navigation menu item content. (default: var(--dds-background-neutral-base)). |
--dds-navigation-menu-item-shadow | The box shadow of the navigation menu item content. (default: var(--dds-shadow-base)). |