Skip to content

NavigationMenu ​

A website-style multi-level navigation bar: top-level triggers open a unified viewport panel (mega-panel multi-column link cards) on hover/click, with delayed open/close, a controlled open value, and full keyboard navigation. Leaf items with href render as links.

Basic usage (mega panel) ​

Basic usage

Nothing selected

Controlled open (value + oas-change) ​

Event semantics: oas-change is an open/close event — it fires when a group expands or collapses the panel (value = the open top-level item); leaf selection goes through oas-select. When embedding the menu inside a host container (popover/drawer etc.), do NOT listen to oas-change to "navigate and close the container" — that closes the container right as the panel expands. Listen to oas-select for close-on-navigate instead.

Controlled open

Open ProductsOpen ResourcesClose
Currently open: products

Delayed open/close ​

Delayed open/close

Vertical orientation ​

Vertical

Multi-column grid ​

Multi-column grid (columns=3)

Icon color (iconColor) ​

Panel link-card icons accept an iconColor field (fixes the icon color, overriding the selected/disabled default); it defaults to currentColor following the text color. The declarative child channel uses the icon-color attribute.

Icon color (iconColor)

Backdrop + keep-mounted + arrow ​

Backdrop + keep-mounted + arrow

backdrop shows an overlay when open; keep-mounted keeps the panel DOM after closing; arrow shows the pointer arrow.

Disabled items ​

Disabled items

Arrow follows the trigger ​

By default the popup arrow doesn't point at any trigger; once the panel opens, JS writes the arrow position from the current trigger's offset/width, and the arrow follows when you switch triggers.

Arrow follows the trigger

Hover/click "Products" then "Resources": the arrow follows the open trigger.

Collision flip (the boundary is the viewport) ​

By default the panel's writing-start edge follows the active trigger (left edge in LTR / right edge in RTL): neither the nav bar nor any outer container is a clipping boundary, so the panel may overflow both (a shrink-to-fit nav bar is always overflowed). The panel flips only when the followed panel would overflow the viewport's right edge — then its right edge aligns to the trigger's right edge and still stays inside the viewport; when the panel's bottom would overflow the viewport it flips up (flip-up); with enough room it keeps the normal start-edge-following position. Narrow the browser window until the panel no longer fits to see the flip.

A narrow container is not a clipping boundary (left edge follows the trigger)

The container is only 120px (narrower than the panel) and the nav bar itself is only slightly wider than its trigger, while the panel's width grows with its content (bottoming out at the content's min-content, not a fixed width): overflowing the container and the nav bar neither flips nor clips the panel (neither is a clipping boundary) — its left edge keeps following the trigger. The flip is viewport-driven only: narrow the browser window until the panel no longer fits and it right-aligns.

Sub second-level cascade ​

A panel item with a sub field (second-level nav data) renders a "sub trigger"; clicking opens an overlay second-level panel inside the panel (slide-in cascade animation): Esc / ← steps back to the main panel (focus returns to the trigger), another Esc closes the whole panel; ↓ moves between the second-level links (skipping disabled ones), Enter selects. Coexists with the inline section folding (sub takes precedence over children).

Sub second-level cascade

Click "Learn" to expand the second-level nav inside the panel

slot="panel-footer" renders a footer container at the bottom of the panel (CTA cards etc.) — only when it has content; it opens together with the panel, and the --vp-h height transition includes it.

panel-footer marketing slot

Want to talk about your needs first?Book a demo

A marketing area appears at the bottom of the panel (only rendered when it has content).

Declarative child channel ​

Besides the items JSON, you can write items declaratively with <oas-navigation-menu-item> (the items attribute wins when explicitly set). The default slot text is the label; attributes map to the NavItem/MenuItem scalar fields (value/href/target/icon/description/active/disabled etc.). Direct child items recurse into children (inline second-level nav inside the panel); an item with the sub attribute parses its children into the overlay second-level nav; <oas-navigation-menu-group> is the group carrier (type: "group" semantics — its items are flattened into the grid). Child additions/removals, attribute and text changes re-render automatically.

Child channel basic

Products ComponentsDocsPricingAboutNot selected yet

Second-level cascade (sub) and groups

Products ComponentsThemesLearn DocsTutorials

With the `sub` attribute the children render as an overlay second-level nav inside the panel; group carrier items are flattened into the grid.

API ​

oas-navigation-menu ​

Attributes ​

AttributeDescriptionTypeDefault
arrowPointer arrow on the popup, shown by default; arrow="false" hides itstringtrue
backdropShow an overlay when open (clicking the overlay closes)boolean—
columnsPanel grid columns, default 2 (mega-panel multi-column link cards)string2
delay-durationHover open/close delay in ms, default 200; clicks and keyboard are immediatestring200
itemsNavigation items JSON (hierarchical; leaf items can carry description text and an icon to render mega-panel link cards)string[]
keep-mountedKeep the panel DOM mounted when closed (crawler/SEO indexing)boolean—
loopTop-level arrow-key wrap-around toggle, default true (loops at edges); explicit loop="false" stops at the edges (aligned with menubar)string—
orientationLayout direction: horizontal (default) / vertical (panel appears to the right of triggers)stringhorizontal
skip-delay-durationSkip-delay window in ms, default 300: hovering another trigger within this window after a close opens it immediatelystring300
valueControlled open item (top-level trigger value; empty string = closed; when present the open state follows the attribute and interactions only dispatch oas-change for the host to update)string—

Events ​

EventDescription
oas-changeThe open item changed (open/close semantics: dispatched when a group expands or collapses, NOT a leaf selection), detail: { value } (value is the open top-level item value; empty string = closed); listen to oas-select for leaf selection — do not use this event to navigate or close containers
oas-selectAn item was selected (top-level leaf link, panel link card or secondary sub-nav link), detail: { value }

Slots ​

NameDescription
panel-footerMarketing slot at the bottom of the panel: <div slot="panel-footer"> (CTA cards etc.) renders a footer container inside the panel when it has content

CSS Variables ​

CSS VariableDefault
--oas-nav-panel-max-height60vh
--oas-navigation-menu-arrow-height12px
--oas-navigation-menu-arrow-radius0
--oas-navigation-menu-arrow-width12px

oas-navigation-menu-item ​

Attributes ​

AttributeDescriptionTypeDefault
activeCurrent-page marker: the link renders aria-current="page" (applies to top-level and panel links)——
dangerDestructive item (red semantics)——
descriptionLink-card description: rendered under the title in mega-panel mode——
disabledDisabled: renders aria-disabled and blocks clicks/keyboard/hover——
hrefLink URL: leaf items with href render as <a> (top-level and panel link cards)——
iconIcon name (@oas-ui/icons registry icon name): panel link-card icon——
icon-colorIcon color: fixes the icon to this color (overrides the selected/disabled default); defaults to currentColor following the text color——
kindLeaf item semantics: radio (default) / action / checkbox——
loadingLoading: blocks interaction until data-driven recovery——
relLink rel (custom, e.g. noopener)——
subBoolean: when present its direct child items parse into sub (overlay second-level nav inside the panel); otherwise they recurse into children (inline secondary sub-nav)——
targetLink open target (e.g. _blank)——
valueSelection value (required): top-level triggers and panel items use it for open/select/keyboard——

Slots ​

NameDescription
defaultNavigation item label content (default slot text; direct child items/group carriers are excluded)

oas-navigation-menu-group ​

Attributes ​

AttributeDescriptionTypeDefault
labelOptional group title (not rendered by this component's panel — it only carries data, matching the type: "group" field of the JSON channel)——

Slots ​

NameDescription
defaultGrouped navigation items (oas-navigation-menu-item children, flattened into the grid when the panel renders)

NavItem fields (inherits MenuItem):

FieldDescriptionType
labelNavigation textstring
valueSelection valuestring
hrefLink URL (optional); leaf items with href render as <a> and are navigablestring
targetLink open target (optional)string
iconIcon name (optional); panel link-card iconstring
iconColorIcon color (optional): fixes the icon to this color, overriding the selected/disabled default; defaults to currentColor following the text colorstring
descriptionLink-card description (optional); rendered under the title in mega-panel modestring
activeCurrent-page marker (optional); renders aria-current="page" on the linkboolean
disabledDisabledboolean
childrenSub navigation items (optional); a top-level item with children opens the mega panel, and panel children with children render as an inline secondary sub-navNavItem[]

Interaction: hover (after delay-duration) / click a top-level trigger to open the unified viewport panel; click a panel link card to select and fire oas-select; the open item change fires oas-change. Clicking outside closes; Esc closes the panel; ←/→ (vertical ↑/↓) switch top level, ↓ (vertical →) opens the panel and focuses the first item, ↓/↑ move within the panel, → expands an inline secondary sub-nav, Enter selects; while the panel is open Tab cycles among panel items (focus trap).

Released under the MIT OR Apache-2.0 License.