Navigation menu
Mega-menu navigation using popover panels, nested submenus, and anchor positioning.
The navigation menu is rooted on a semantic <nav class="ui-navigation-menu"> (class form only, since the native element carries the meaning) and combines button triggers (popovertarget) with [data-slot="navigation-menu-popover"] panels. [data-slot="navigation-menu-list"] contains items and triggers. Panels contain a [data-slot="navigation-menu-viewport"] grid of [data-slot="navigation-menu-link"] items and optional [data-slot="navigation-menu-submenu"] flyouts.
Default
<header class="w-full"> <nav class="ui-navigation-menu container"> <menu data-slot="navigation-menu-list"> <!-- Rich panel: featured callout + link grid --> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-panel-1" > Platform <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-panel-1" data-slot="navigation-menu-popover" popover="auto" data-animation="slide-down" > <div class="grid grid-cols-3" data-slot="navigation-menu-viewport"> <div class="grid py-xs pl-xs"> <a href="#" class="flex flex-col items-start justify-end bg-muted transition hover:opacity-75 rounded-sm p-sm" > <span class="text-eyebrow text-muted-foreground mb-xs">Featured</span> <span class="text-md font-strong">Platform</span> <span class="text-sm text-muted-foreground text-pretty"> Everything you need to design, build, and ship. </span> </a> </div> <div class="flex flex-col gap-px py-xs pr-xs col-span-2"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Analytics</span> <span class="text-sm text-muted-foreground" >Real-time insights into product usage.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Automation</span> <span class="text-sm text-muted-foreground">Workflows that run themselves.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Integrations</span> <span class="text-sm text-muted-foreground" >Connect the tools your team already uses.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Features</span> <span class="text-sm text-muted-foreground" >Everything you need to design, build, and ship.</span > </a> </div> </div> </div> </li> <!-- Simple panel: single-column link list --> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-panel-2" > Resources <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-panel-2" data-slot="navigation-menu-popover" data-align="start" popover="auto" data-animation="slide-down" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Documentation</span> <span class="text-sm text-muted-foreground">Guides and API references.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Blog</span> <span class="text-sm text-muted-foreground" >Product news and engineering notes.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Community</span> <span class="text-sm text-muted-foreground" >Join the conversation and get help.</span > </a> </div> </div> </div> </li> <!-- Submenu panel — categories whose products fly out to the right --> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-panel-3" > Products <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-panel-3" data-slot="navigation-menu-popover" popover="auto" data-animation="slide-down" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <!-- Category — opens its product list to the right --> <div data-slot="navigation-menu-submenu"> <button data-slot="navigation-menu-submenu-trigger" type="button" popovertarget="nav-submenu-1" > <span class="text-sm font-strong">Design</span> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="96 48 176 128 96 208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-submenu-1" data-slot="navigation-menu-popover" data-variant="submenu" popover="auto" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Figma Kit</span> <span class="text-sm text-muted-foreground" >Variables, styles, and components.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Color Generator</span> <span class="text-sm text-muted-foreground" >OKLCH scales from a single brand hex.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Type Scale</span> <span class="text-sm text-muted-foreground" >Fluid sizes tuned with Utopia.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Icon Set</span> <span class="text-sm text-muted-foreground" >A consistent, lightweight icon library.</span > </a> </div> </div> </div> </div> <!-- Category — opens its product list to the right --> <div data-slot="navigation-menu-submenu"> <button data-slot="navigation-menu-submenu-trigger" type="button" popovertarget="nav-submenu-2" > <span class="text-sm font-strong">Development</span> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="96 48 176 128 96 208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-submenu-2" data-slot="navigation-menu-popover" data-variant="submenu" popover="auto" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">CLI</span> <span class="text-sm text-muted-foreground" >Scaffold and ship from the terminal.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">React Components</span> <span class="text-sm text-muted-foreground" >Token-driven primitives for your app.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">REST API</span> <span class="text-sm text-muted-foreground" >Programmatic access to your design data.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Webhooks</span> <span class="text-sm text-muted-foreground" >React to changes in real time.</span > </a> </div> </div> </div> </div> <!-- Category — opens its product list to the right --> <div data-slot="navigation-menu-submenu"> <button data-slot="navigation-menu-submenu-trigger" type="button" popovertarget="nav-submenu-3" > <span class="text-sm font-strong">Content</span> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="96 48 176 128 96 208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-submenu-3" data-slot="navigation-menu-popover" data-variant="submenu" popover="auto" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">CMS</span> <span class="text-sm text-muted-foreground" >Structured content for any surface.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Localization</span> <span class="text-sm text-muted-foreground" >Ship in every language your users speak.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Media Library</span> <span class="text-sm text-muted-foreground" >Optimized assets, delivered fast.</span > </a> </div> </div> </div> </div> </div> </div> </div> </li> <li class="ml-auto mr-xs" data-slot="navigation-menu-item"> <!-- Form dialog START --> <button class="ui-button" type="button" data-variant="ghost" command="show-modal" commandfor="form-sign-in-1" > Sign in <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <line x1="24" y1="128" x2="136" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <polyline points="96 88 136 128 96 168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <polyline points="136 40 200 40 200 216 136 216" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <dialog id="form-sign-in-1" class="ui-dialog" closedby="any"> <form method="dialog" data-slot="dialog-content"> <header data-slot="dialog-header"> <h2 class="text-lg font-heading font-strong">Sign in</h2> <p class="text-muted-foreground">Enter your email and password to sign in.</p> </header> <div class="flex flex-col gap-md" data-slot="dialog-body"> <fieldset class="ui-field-group"> <legend class="sr-only">Sign in</legend> <div class="grid gap-xs"> <div class="ui-field"> <label data-slot="field-label" for="fd-email">Email</label> <input class="ui-input" id="fd-email" name="email" type="email" inputmode="email" required placeholder="you@example.com" autocomplete="email" aria-describedby="fd-email-hint" /> <div data-slot="field-description"> <span data-slot="field-hint" id="fd-email-hint" >We'll only use this to send receipts.</span > <span data-slot="field-error" role="alert" >Please enter a valid email address.</span > </div> </div> <!-- Password START --> <div class="ui-field"> <label data-slot="field-label" for="f-login-password">Password</label> <ui-password> <label class="ui-password-group"> <input class="ui-input" id="f-login-password" name="password" type="password" autocomplete="new-password" minlength="8" required placeholder="••••••••" aria-describedby="f-login-password-hint f-login-password-warning" /> <span data-slot="password-group-addon" data-align="inline-end"> <button class="ui-button" data-slot="password-group-toggle" id="f-login-password-toggle" data-variant="ghost" data-size="icon-sm" type="button" aria-pressed="false" aria-label="Show password" aria-describedby="f-login-password-warning" > <svg data-slot="password-group-icon password-group-icon-show" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path d="M128,56C48,56,16,128,16,128s32,72,112,72,112-72,112-72S208,56,128,56Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <circle cx="128" cy="128" r="40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> <svg data-slot="password-group-icon password-group-icon-hide" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <line x1="48" y1="40" x2="208" y2="216" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <path d="M154.9,157.6A40,40,0,0,1,101,98.4" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <path d="M73.8,69.7C33.6,90.6,16,128,16,128s32,72,112,72a118.1,118.1,0,0,0,54.1-12.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <path d="M208.6,169.1C229.8,149.1,240,128,240,128S208,56,128,56a126,126,0,0,0-20.5,1.6" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> </span> </label> </ui-password> <div data-slot="field-description"> <span data-slot="field-error" role="alert">Incorrect email or password.</span> </div> <span class="sr-only" id="f-login-password-warning" >Warning: showing the password makes it visible to anyone near your screen.</span > </div> <!-- Password END --> </div> </fieldset> </div> <footer data-slot="dialog-footer"> <button class="ui-button" type="button" value="cancel" commandfor="form-sign-in-1" command="close" > Cancel </button> <button class="ui-button" data-variant="primary" type="submit" value="save"> Sign in </button> </footer> </form> <button class="ui-button" data-slot="dialog-close" data-size="icon" data-variant="ghost" type="button" commandfor="form-sign-in-1" command="close" aria-label="Close dialog" > <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <line x1="200" y1="56" x2="56" y2="200" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <line x1="200" y1="200" x2="56" y2="56" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> </dialog> <!-- Form dialog END --> </li> <!-- Plain CTA — no popover --> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="primary">Get started</a> </li> </menu> </nav></header>/** * navigation-menu.css: Navigation menu (.ui-navigation-menu) * * @layer variables, components * @requires layers.css, _variables.css, popover.css, button.css * @uses [popover] + nested popovers: megamenu panels and flyout submenus * @uses anchor-name, anchor-scope, position-anchor, anchor-size(): panel * sizing and positioning (Chromium 125+ for anchor-size) * @uses :has(:popover-open): chevron rotation without JS * @uses view-transition-name: stable header across navigations * @uses @starting-style: slide-down viewport reveal * @tokens --ui-navigation-menu-* (@layer variables) */@layer variables { :root { --ui-navigation-menu-icon-transition: transform var(--default-transition-duration) var(--default-transition-timing-function); --ui-navigation-menu-icon-transform: rotate(-180deg); --ui-navigation-menu-popover-shadow: var(--shadow-md); --ui-navigation-menu-popover-child-radius: var(--radius-sm); --ui-navigation-menu-popover-gap: var(--step-1_5); --ui-navigation-menu-popover-inline-size: min(var(--breakpoint-xs), 100% - var(--gap-md)); --ui-navigation-menu-popover-viewport-display: grid; --ui-navigation-menu-popover-viewport-align-items: stretch; --ui-navigation-menu-popover-viewport-justify-items: stretch; --ui-navigation-menu-popover-viewport-template-columns: 1fr; --ui-navigation-menu-popover-viewport-gap: var(--step-1_5); --ui-navigation-menu-popover-viewport-min-inline-size: var(--step-52); }}@layer zazz.components { .ui-navigation-menu { /* View transitions - keeps the header stable when navigating between pages */ --ui-popover-shadow: var(--ui-navigation-menu-popover-shadow); --ui-popover-child-radius: var(--ui-navigation-menu-popover-child-radius); --ui-popover-gap: var(--ui-navigation-menu-popover-gap); --ui-popover-inline-size: var(--ui-navigation-menu-popover-inline-size); --ui-popover-viewport-display: var(--ui-navigation-menu-popover-viewport-display); --ui-popover-viewport-align-items: var(--ui-navigation-menu-popover-viewport-align-items); --ui-popover-viewport-justify-items: var(--ui-navigation-menu-popover-viewport-justify-items); --ui-popover-viewport-template-columns: var( --ui-navigation-menu-popover-viewport-template-columns ); --ui-popover-viewport-gap: var(--ui-navigation-menu-popover-viewport-gap); --ui-popover-viewport-min-inline-size: var( --ui-navigation-menu-popover-viewport-min-inline-size ); display: inline-flex; position: relative; anchor-name: --ui-navigation-menu-menu-root; isolation: isolate; } [data-slot~="navigation-menu-list"] { display: flex; flex-direction: row; align-items: center; gap: var(--ui-popover-gap); anchor-name: --ui-navigation-menu-menu-list; } /* Each item scopes its own anchor name so triggers can share one name */ [data-slot~="navigation-menu-item"] { anchor-scope: --ui-navigation-menu-menu-trigger; position: relative; } [data-slot~="navigation-menu-trigger"] { anchor-name: --ui-navigation-menu-menu-trigger; } /* Chevron points down by default, flips up while its popover is open (no JS) */ [data-slot~="navigation-menu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); transition: var(--ui-navigation-menu-icon-transition); } [data-slot~="navigation-menu-item"]:has( [data-slot~="navigation-menu-popover"]:where(:popover-open, .\:popover-open) ) [data-slot~="navigation-menu-trigger"] > svg { transform: var(--ui-navigation-menu-icon-transform); } /* =========================================================================== POPOVER — inherits --ui-popover-* from popover.css; panel layout overrides here =========================================================================== */ [data-slot~="navigation-menu-popover"] { position-anchor: --ui-navigation-menu-menu-trigger; max-inline-size: var(--ui-popover-inline-size); --ui-popover-padding: 0; --ui-popover-min-inline-size: auto; /* Default: open below the trigger, aligned to its start edge */ --ui-popover-position-area: block-end span-inline-end; --ui-popover-origin: top center; --ui-popover-margin-block: var(--step-1_5); } [data-slot~="navigation-menu-popover"] .ui-button { --ui-button-radius: var(--radius-xs); } /* =========================================================================== ALIGNMENT — nav menus only open downward, so they set placement directly here rather than through data-side; popover.css's shared matrix keys on [data-side] and so never applies to these panels. =========================================================================== */ [data-slot~="navigation-menu-popover"][data-align="center"] { --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; } [data-slot~="navigation-menu-popover"][data-align="end"] { --ui-popover-position-area: block-end span-inline-start; --ui-popover-origin: top right; } /* =========================================================================== SUBMENU — categories inside a panel whose product lists fly out to the right. Native nested popovers: each sub-trigger's popovertarget lives inside the parent panel, so opening a submenu keeps the panel open (ancestor chain, no JS). =========================================================================== */ /* Each category scopes its own anchor so every sub-trigger reuses one name */ [data-slot~="navigation-menu-submenu"] { anchor-scope: --ui-navigation-menu-menu-subtrigger; position: relative; } /* Category row — a full-width link row with a trailing caret */ [data-slot~="navigation-menu-submenu-trigger"] { anchor-name: --ui-navigation-menu-menu-subtrigger; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-sm); inline-size: 100%; padding: var(--ui-popover-gap); border: 0; border-radius: var(--ui-popover-child-radius); color: inherit; font: inherit; text-align: start; cursor: pointer; } /* Highlight the row on hover and while its submenu is open */ [data-slot~="navigation-menu-submenu-trigger"]:hover, [data-slot~="navigation-menu-submenu"]:has( [data-slot~="navigation-menu-popover"][data-variant="submenu"]:where( :popover-open, .\:popover-open ) ) [data-slot~="navigation-menu-submenu-trigger"] { background-color: var(--muted); } /* Caret points right — the direction the submenu opens */ [data-slot~="navigation-menu-submenu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); flex: 0 0 auto; color: var(--muted-foreground); } /* Submenu (data-variant="submenu") inherits all base [data-slot~="navigation-menu-popover"] styling; only the anchor and open direction differ — it flies out to the inline-end edge, top-aligned, flipping leftward near the viewport's edge. */ [data-slot~="navigation-menu-popover"][data-variant="submenu"] { position-anchor: --ui-navigation-menu-menu-subtrigger; --ui-popover-position-area: inline-end span-block-start; --ui-popover-origin: left top; --ui-popover-margin-block: 0; --ui-popover-margin-inline: var(--step-1_5); --ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block; } /* =========================================================================== PANEL CONTENT =========================================================================== */ [data-slot~="navigation-menu-viewport"] { display: var(--ui-popover-viewport-display); grid-template-columns: var(--ui-popover-viewport-template-columns); align-items: var(--ui-popover-viewport-align-items); justify-items: var(--ui-popover-viewport-justify-items); gap: var(--ui-popover-viewport-gap); min-inline-size: var(--ui-popover-viewport-min-inline-size); } /* Rich link row: title + description, hover fills with muted */ [data-slot~="navigation-menu-link"] { display: flex; flex-direction: column; padding: var(--ui-popover-gap); border-radius: var(--ui-popover-child-radius); text-decoration: none; transition: background-color var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-link"]:hover { background-color: var(--muted); } /* =========================================================================== MEGAMENU ANIMATION =========================================================================== */ /* Slide-down: accordion-style block-size reveal on the viewport (not scaleY). */ [data-slot~="navigation-menu-popover"][data-size="screen"][data-animation="slide-down"] { --ui-popover-scale--start: 1; } [data-slot~="navigation-menu-popover"][data-animation="slide-down"] > [data-slot~="navigation-menu-viewport"] { overflow: clip; block-size: 0; transform: translateY(0.5rem); transform-origin: top center; transition: block-size var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function), padding var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) { opacity: var(--ui-popover-opacity--end); transform: scale(var(--ui-popover-scale--end)) translate3d(var(--ui-popover-translate--end)); @starting-style { opacity: var(--ui-popover-opacity--start); transform: scale(var(--ui-popover-scale--start)) translate3d(var(--ui-popover-translate--start)); } } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) [data-slot~="navigation-menu-viewport"] { block-size: auto; transform: scale(1) translateY(0); @starting-style { block-size: 0; transform: translateY(0.5rem); } } /* =========================================================================== SIZING VARIANTS =========================================================================== */ [data-slot~="navigation-menu-popover"][data-size="container"] { position-anchor: --ui-navigation-menu-menu-list; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="root"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="screen"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: 100vi; --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); }}Hover open
Add interestfor alongside popovertarget (same panel id) on any trigger to open its panel on hover, focus, or long-press. This uses the Interest Invokers API. Clicking still works, and the popover auto stack closes the previous panel as the next opens.
<!-- Hover-open variant: every trigger carries `interestfor` alongside `popovertarget` (same id), so panels open on hover/focus and on click. --><header class="w-full"> <nav class="ui-navigation-menu container"> <menu data-slot="navigation-menu-list"> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-interest-1" interestfor="nav-interest-1" > Platform <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-interest-1" data-slot="navigation-menu-popover" data-align="start" popover="auto" data-animation="slide-down" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Analytics</span> <span class="text-sm text-muted-foreground">Real-time product insights.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Automation</span> <span class="text-sm text-muted-foreground">Workflows that run themselves.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Integrations</span> <span class="text-sm text-muted-foreground">Connect your team's tools.</span> </a> </div> </div> </div> </li> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-interest-2" interestfor="nav-interest-2" > Resources <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-interest-2" data-slot="navigation-menu-popover" data-align="start" popover="auto" data-animation="slide-down" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Documentation</span> <span class="text-sm text-muted-foreground">Guides and API references.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Blog</span> <span class="text-sm text-muted-foreground">Product news and notes.</span> </a> </div> </div> </div> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">Pricing</a> </li> </menu> </nav></header>/** * navigation-menu.css: Navigation menu (.ui-navigation-menu) * * @layer variables, components * @requires layers.css, _variables.css, popover.css, button.css * @uses [popover] + nested popovers: megamenu panels and flyout submenus * @uses anchor-name, anchor-scope, position-anchor, anchor-size(): panel * sizing and positioning (Chromium 125+ for anchor-size) * @uses :has(:popover-open): chevron rotation without JS * @uses view-transition-name: stable header across navigations * @uses @starting-style: slide-down viewport reveal * @tokens --ui-navigation-menu-* (@layer variables) */@layer variables { :root { --ui-navigation-menu-icon-transition: transform var(--default-transition-duration) var(--default-transition-timing-function); --ui-navigation-menu-icon-transform: rotate(-180deg); --ui-navigation-menu-popover-shadow: var(--shadow-md); --ui-navigation-menu-popover-child-radius: var(--radius-sm); --ui-navigation-menu-popover-gap: var(--step-1_5); --ui-navigation-menu-popover-inline-size: min(var(--breakpoint-xs), 100% - var(--gap-md)); --ui-navigation-menu-popover-viewport-display: grid; --ui-navigation-menu-popover-viewport-align-items: stretch; --ui-navigation-menu-popover-viewport-justify-items: stretch; --ui-navigation-menu-popover-viewport-template-columns: 1fr; --ui-navigation-menu-popover-viewport-gap: var(--step-1_5); --ui-navigation-menu-popover-viewport-min-inline-size: var(--step-52); }}@layer zazz.components { .ui-navigation-menu { /* View transitions - keeps the header stable when navigating between pages */ --ui-popover-shadow: var(--ui-navigation-menu-popover-shadow); --ui-popover-child-radius: var(--ui-navigation-menu-popover-child-radius); --ui-popover-gap: var(--ui-navigation-menu-popover-gap); --ui-popover-inline-size: var(--ui-navigation-menu-popover-inline-size); --ui-popover-viewport-display: var(--ui-navigation-menu-popover-viewport-display); --ui-popover-viewport-align-items: var(--ui-navigation-menu-popover-viewport-align-items); --ui-popover-viewport-justify-items: var(--ui-navigation-menu-popover-viewport-justify-items); --ui-popover-viewport-template-columns: var( --ui-navigation-menu-popover-viewport-template-columns ); --ui-popover-viewport-gap: var(--ui-navigation-menu-popover-viewport-gap); --ui-popover-viewport-min-inline-size: var( --ui-navigation-menu-popover-viewport-min-inline-size ); display: inline-flex; position: relative; anchor-name: --ui-navigation-menu-menu-root; isolation: isolate; } [data-slot~="navigation-menu-list"] { display: flex; flex-direction: row; align-items: center; gap: var(--ui-popover-gap); anchor-name: --ui-navigation-menu-menu-list; } /* Each item scopes its own anchor name so triggers can share one name */ [data-slot~="navigation-menu-item"] { anchor-scope: --ui-navigation-menu-menu-trigger; position: relative; } [data-slot~="navigation-menu-trigger"] { anchor-name: --ui-navigation-menu-menu-trigger; } /* Chevron points down by default, flips up while its popover is open (no JS) */ [data-slot~="navigation-menu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); transition: var(--ui-navigation-menu-icon-transition); } [data-slot~="navigation-menu-item"]:has( [data-slot~="navigation-menu-popover"]:where(:popover-open, .\:popover-open) ) [data-slot~="navigation-menu-trigger"] > svg { transform: var(--ui-navigation-menu-icon-transform); } /* =========================================================================== POPOVER — inherits --ui-popover-* from popover.css; panel layout overrides here =========================================================================== */ [data-slot~="navigation-menu-popover"] { position-anchor: --ui-navigation-menu-menu-trigger; max-inline-size: var(--ui-popover-inline-size); --ui-popover-padding: 0; --ui-popover-min-inline-size: auto; /* Default: open below the trigger, aligned to its start edge */ --ui-popover-position-area: block-end span-inline-end; --ui-popover-origin: top center; --ui-popover-margin-block: var(--step-1_5); } [data-slot~="navigation-menu-popover"] .ui-button { --ui-button-radius: var(--radius-xs); } /* =========================================================================== ALIGNMENT — nav menus only open downward, so they set placement directly here rather than through data-side; popover.css's shared matrix keys on [data-side] and so never applies to these panels. =========================================================================== */ [data-slot~="navigation-menu-popover"][data-align="center"] { --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; } [data-slot~="navigation-menu-popover"][data-align="end"] { --ui-popover-position-area: block-end span-inline-start; --ui-popover-origin: top right; } /* =========================================================================== SUBMENU — categories inside a panel whose product lists fly out to the right. Native nested popovers: each sub-trigger's popovertarget lives inside the parent panel, so opening a submenu keeps the panel open (ancestor chain, no JS). =========================================================================== */ /* Each category scopes its own anchor so every sub-trigger reuses one name */ [data-slot~="navigation-menu-submenu"] { anchor-scope: --ui-navigation-menu-menu-subtrigger; position: relative; } /* Category row — a full-width link row with a trailing caret */ [data-slot~="navigation-menu-submenu-trigger"] { anchor-name: --ui-navigation-menu-menu-subtrigger; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-sm); inline-size: 100%; padding: var(--ui-popover-gap); border: 0; border-radius: var(--ui-popover-child-radius); color: inherit; font: inherit; text-align: start; cursor: pointer; } /* Highlight the row on hover and while its submenu is open */ [data-slot~="navigation-menu-submenu-trigger"]:hover, [data-slot~="navigation-menu-submenu"]:has( [data-slot~="navigation-menu-popover"][data-variant="submenu"]:where( :popover-open, .\:popover-open ) ) [data-slot~="navigation-menu-submenu-trigger"] { background-color: var(--muted); } /* Caret points right — the direction the submenu opens */ [data-slot~="navigation-menu-submenu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); flex: 0 0 auto; color: var(--muted-foreground); } /* Submenu (data-variant="submenu") inherits all base [data-slot~="navigation-menu-popover"] styling; only the anchor and open direction differ — it flies out to the inline-end edge, top-aligned, flipping leftward near the viewport's edge. */ [data-slot~="navigation-menu-popover"][data-variant="submenu"] { position-anchor: --ui-navigation-menu-menu-subtrigger; --ui-popover-position-area: inline-end span-block-start; --ui-popover-origin: left top; --ui-popover-margin-block: 0; --ui-popover-margin-inline: var(--step-1_5); --ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block; } /* =========================================================================== PANEL CONTENT =========================================================================== */ [data-slot~="navigation-menu-viewport"] { display: var(--ui-popover-viewport-display); grid-template-columns: var(--ui-popover-viewport-template-columns); align-items: var(--ui-popover-viewport-align-items); justify-items: var(--ui-popover-viewport-justify-items); gap: var(--ui-popover-viewport-gap); min-inline-size: var(--ui-popover-viewport-min-inline-size); } /* Rich link row: title + description, hover fills with muted */ [data-slot~="navigation-menu-link"] { display: flex; flex-direction: column; padding: var(--ui-popover-gap); border-radius: var(--ui-popover-child-radius); text-decoration: none; transition: background-color var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-link"]:hover { background-color: var(--muted); } /* =========================================================================== MEGAMENU ANIMATION =========================================================================== */ /* Slide-down: accordion-style block-size reveal on the viewport (not scaleY). */ [data-slot~="navigation-menu-popover"][data-size="screen"][data-animation="slide-down"] { --ui-popover-scale--start: 1; } [data-slot~="navigation-menu-popover"][data-animation="slide-down"] > [data-slot~="navigation-menu-viewport"] { overflow: clip; block-size: 0; transform: translateY(0.5rem); transform-origin: top center; transition: block-size var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function), padding var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) { opacity: var(--ui-popover-opacity--end); transform: scale(var(--ui-popover-scale--end)) translate3d(var(--ui-popover-translate--end)); @starting-style { opacity: var(--ui-popover-opacity--start); transform: scale(var(--ui-popover-scale--start)) translate3d(var(--ui-popover-translate--start)); } } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) [data-slot~="navigation-menu-viewport"] { block-size: auto; transform: scale(1) translateY(0); @starting-style { block-size: 0; transform: translateY(0.5rem); } } /* =========================================================================== SIZING VARIANTS =========================================================================== */ [data-slot~="navigation-menu-popover"][data-size="container"] { position-anchor: --ui-navigation-menu-menu-list; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="root"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="screen"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: 100vi; --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); }}Featured panel
A common content-site layout: link rows beside a featured card, arranged with grid utilities inside the viewport.
<!-- Content-site panel layout: link rows beside a featured card. --><header class="w-full"> <nav class="ui-navigation-menu container"> <menu data-slot="navigation-menu-list"> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-featured-1" > Magazine <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-featured-1" data-slot="navigation-menu-popover" data-align="start" popover="auto" data-animation="slide-down" > <div class="grid grid-cols-3" data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px py-xs pl-xs col-span-2"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Latest issue</span> <span class="text-sm text-muted-foreground">This month's stories.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Interviews</span> <span class="text-sm text-muted-foreground">Conversations with makers.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Essays</span> <span class="text-sm text-muted-foreground">Long reads on craft.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Archive</span> <span class="text-sm text-muted-foreground">Every issue since day one.</span> </a> </div> <div class="grid py-xs pr-xs"> <a href="#" class="flex flex-col items-start justify-end bg-muted transition hover:opacity-75 rounded-sm p-sm" > <span class="text-eyebrow text-muted-foreground mb-xs">Featured</span> <span class="text-md font-strong">The typography issue</span> <span class="text-sm text-muted-foreground text-pretty"> A whole issue on letters, grids, and rhythm. </span> </a> </div> </div> </div> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">Newsletter</a> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">About</a> </li> </menu> </nav></header>/** * navigation-menu.css: Navigation menu (.ui-navigation-menu) * * @layer variables, components * @requires layers.css, _variables.css, popover.css, button.css * @uses [popover] + nested popovers: megamenu panels and flyout submenus * @uses anchor-name, anchor-scope, position-anchor, anchor-size(): panel * sizing and positioning (Chromium 125+ for anchor-size) * @uses :has(:popover-open): chevron rotation without JS * @uses view-transition-name: stable header across navigations * @uses @starting-style: slide-down viewport reveal * @tokens --ui-navigation-menu-* (@layer variables) */@layer variables { :root { --ui-navigation-menu-icon-transition: transform var(--default-transition-duration) var(--default-transition-timing-function); --ui-navigation-menu-icon-transform: rotate(-180deg); --ui-navigation-menu-popover-shadow: var(--shadow-md); --ui-navigation-menu-popover-child-radius: var(--radius-sm); --ui-navigation-menu-popover-gap: var(--step-1_5); --ui-navigation-menu-popover-inline-size: min(var(--breakpoint-xs), 100% - var(--gap-md)); --ui-navigation-menu-popover-viewport-display: grid; --ui-navigation-menu-popover-viewport-align-items: stretch; --ui-navigation-menu-popover-viewport-justify-items: stretch; --ui-navigation-menu-popover-viewport-template-columns: 1fr; --ui-navigation-menu-popover-viewport-gap: var(--step-1_5); --ui-navigation-menu-popover-viewport-min-inline-size: var(--step-52); }}@layer zazz.components { .ui-navigation-menu { /* View transitions - keeps the header stable when navigating between pages */ --ui-popover-shadow: var(--ui-navigation-menu-popover-shadow); --ui-popover-child-radius: var(--ui-navigation-menu-popover-child-radius); --ui-popover-gap: var(--ui-navigation-menu-popover-gap); --ui-popover-inline-size: var(--ui-navigation-menu-popover-inline-size); --ui-popover-viewport-display: var(--ui-navigation-menu-popover-viewport-display); --ui-popover-viewport-align-items: var(--ui-navigation-menu-popover-viewport-align-items); --ui-popover-viewport-justify-items: var(--ui-navigation-menu-popover-viewport-justify-items); --ui-popover-viewport-template-columns: var( --ui-navigation-menu-popover-viewport-template-columns ); --ui-popover-viewport-gap: var(--ui-navigation-menu-popover-viewport-gap); --ui-popover-viewport-min-inline-size: var( --ui-navigation-menu-popover-viewport-min-inline-size ); display: inline-flex; position: relative; anchor-name: --ui-navigation-menu-menu-root; isolation: isolate; } [data-slot~="navigation-menu-list"] { display: flex; flex-direction: row; align-items: center; gap: var(--ui-popover-gap); anchor-name: --ui-navigation-menu-menu-list; } /* Each item scopes its own anchor name so triggers can share one name */ [data-slot~="navigation-menu-item"] { anchor-scope: --ui-navigation-menu-menu-trigger; position: relative; } [data-slot~="navigation-menu-trigger"] { anchor-name: --ui-navigation-menu-menu-trigger; } /* Chevron points down by default, flips up while its popover is open (no JS) */ [data-slot~="navigation-menu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); transition: var(--ui-navigation-menu-icon-transition); } [data-slot~="navigation-menu-item"]:has( [data-slot~="navigation-menu-popover"]:where(:popover-open, .\:popover-open) ) [data-slot~="navigation-menu-trigger"] > svg { transform: var(--ui-navigation-menu-icon-transform); } /* =========================================================================== POPOVER — inherits --ui-popover-* from popover.css; panel layout overrides here =========================================================================== */ [data-slot~="navigation-menu-popover"] { position-anchor: --ui-navigation-menu-menu-trigger; max-inline-size: var(--ui-popover-inline-size); --ui-popover-padding: 0; --ui-popover-min-inline-size: auto; /* Default: open below the trigger, aligned to its start edge */ --ui-popover-position-area: block-end span-inline-end; --ui-popover-origin: top center; --ui-popover-margin-block: var(--step-1_5); } [data-slot~="navigation-menu-popover"] .ui-button { --ui-button-radius: var(--radius-xs); } /* =========================================================================== ALIGNMENT — nav menus only open downward, so they set placement directly here rather than through data-side; popover.css's shared matrix keys on [data-side] and so never applies to these panels. =========================================================================== */ [data-slot~="navigation-menu-popover"][data-align="center"] { --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; } [data-slot~="navigation-menu-popover"][data-align="end"] { --ui-popover-position-area: block-end span-inline-start; --ui-popover-origin: top right; } /* =========================================================================== SUBMENU — categories inside a panel whose product lists fly out to the right. Native nested popovers: each sub-trigger's popovertarget lives inside the parent panel, so opening a submenu keeps the panel open (ancestor chain, no JS). =========================================================================== */ /* Each category scopes its own anchor so every sub-trigger reuses one name */ [data-slot~="navigation-menu-submenu"] { anchor-scope: --ui-navigation-menu-menu-subtrigger; position: relative; } /* Category row — a full-width link row with a trailing caret */ [data-slot~="navigation-menu-submenu-trigger"] { anchor-name: --ui-navigation-menu-menu-subtrigger; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-sm); inline-size: 100%; padding: var(--ui-popover-gap); border: 0; border-radius: var(--ui-popover-child-radius); color: inherit; font: inherit; text-align: start; cursor: pointer; } /* Highlight the row on hover and while its submenu is open */ [data-slot~="navigation-menu-submenu-trigger"]:hover, [data-slot~="navigation-menu-submenu"]:has( [data-slot~="navigation-menu-popover"][data-variant="submenu"]:where( :popover-open, .\:popover-open ) ) [data-slot~="navigation-menu-submenu-trigger"] { background-color: var(--muted); } /* Caret points right — the direction the submenu opens */ [data-slot~="navigation-menu-submenu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); flex: 0 0 auto; color: var(--muted-foreground); } /* Submenu (data-variant="submenu") inherits all base [data-slot~="navigation-menu-popover"] styling; only the anchor and open direction differ — it flies out to the inline-end edge, top-aligned, flipping leftward near the viewport's edge. */ [data-slot~="navigation-menu-popover"][data-variant="submenu"] { position-anchor: --ui-navigation-menu-menu-subtrigger; --ui-popover-position-area: inline-end span-block-start; --ui-popover-origin: left top; --ui-popover-margin-block: 0; --ui-popover-margin-inline: var(--step-1_5); --ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block; } /* =========================================================================== PANEL CONTENT =========================================================================== */ [data-slot~="navigation-menu-viewport"] { display: var(--ui-popover-viewport-display); grid-template-columns: var(--ui-popover-viewport-template-columns); align-items: var(--ui-popover-viewport-align-items); justify-items: var(--ui-popover-viewport-justify-items); gap: var(--ui-popover-viewport-gap); min-inline-size: var(--ui-popover-viewport-min-inline-size); } /* Rich link row: title + description, hover fills with muted */ [data-slot~="navigation-menu-link"] { display: flex; flex-direction: column; padding: var(--ui-popover-gap); border-radius: var(--ui-popover-child-radius); text-decoration: none; transition: background-color var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-link"]:hover { background-color: var(--muted); } /* =========================================================================== MEGAMENU ANIMATION =========================================================================== */ /* Slide-down: accordion-style block-size reveal on the viewport (not scaleY). */ [data-slot~="navigation-menu-popover"][data-size="screen"][data-animation="slide-down"] { --ui-popover-scale--start: 1; } [data-slot~="navigation-menu-popover"][data-animation="slide-down"] > [data-slot~="navigation-menu-viewport"] { overflow: clip; block-size: 0; transform: translateY(0.5rem); transform-origin: top center; transition: block-size var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function), padding var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) { opacity: var(--ui-popover-opacity--end); transform: scale(var(--ui-popover-scale--end)) translate3d(var(--ui-popover-translate--end)); @starting-style { opacity: var(--ui-popover-opacity--start); transform: scale(var(--ui-popover-scale--start)) translate3d(var(--ui-popover-translate--start)); } } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) [data-slot~="navigation-menu-viewport"] { block-size: auto; transform: scale(1) translateY(0); @starting-style { block-size: 0; transform: translateY(0.5rem); } } /* =========================================================================== SIZING VARIANTS =========================================================================== */ [data-slot~="navigation-menu-popover"][data-size="container"] { position-anchor: --ui-navigation-menu-menu-list; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="root"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="screen"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: 100vi; --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); }}Icon grid
Tiles of icon, title, and description, each wrapped as a navigation-menu-link laid out with flex utilities.
<!-- Content-site panel layout: a grid of icon tiles. --><header class="w-full"> <nav class="ui-navigation-menu container"> <menu data-slot="navigation-menu-list"> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-icon-grid-1" > Solutions <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-icon-grid-1" data-slot="navigation-menu-popover" data-align="start" popover="auto" data-animation="slide-down" style=" --ui-popover-inline-size: min(var(--breakpoint-sm), 100% - var(--gap-md)); inline-size: var(--ui-popover-inline-size); " > <div class="grid grid-cols-3 p-xs" data-slot="navigation-menu-viewport"> <a href="#" data-slot="navigation-menu-link" class="flex-row items-start gap-sm"> <svg class="size-md text-muted-foreground shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M232,208a8,8,0,0,1-8,8H32a8,8,0,0,1-8-8V48a8,8,0,0,1,16,0v94.37L90.73,98a8,8,0,0,1,10.07-.38l58.81,44.11L218.73,90a8,8,0,1,1,10.54,12l-64,56a8,8,0,0,1-10.07.38L96.39,114.29,40,163.63V200H224A8,8,0,0,1,232,208Z" /> </svg> <span class="flex flex-col"> <span class="text-sm font-strong">Analytics</span> <span class="text-sm text-muted-foreground">Understand your traffic.</span> </span> </a> <a href="#" data-slot="navigation-menu-link" class="flex-row items-start gap-sm"> <svg class="size-md text-muted-foreground shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M240,128a8,8,0,0,1-8,8H204.94l-37.78,75.58A8,8,0,0,1,160,216h-.4a8,8,0,0,1-7.08-5.14L95.35,60.76,63.28,131.31A8,8,0,0,1,56,136H24a8,8,0,0,1,0-16H50.85L88.72,36.69a8,8,0,0,1,14.76.46l57.51,151,31.85-63.71A8,8,0,0,1,200,120h32A8,8,0,0,1,240,128Z" /> </svg> <span class="flex flex-col"> <span class="text-sm font-strong">Uptime</span> <span class="text-sm text-muted-foreground">Monitoring and alerts.</span> </span> </a> <a href="#" data-slot="navigation-menu-link" class="flex-row items-start gap-sm"> <svg class="size-md text-muted-foreground shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M104,40H56A16,16,0,0,0,40,56v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,104,40Zm0,64H56V56h48v48Zm96-64H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,64H152V56h48v48Zm-96,32H56a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,104,136Zm0,64H56V152h48v48Zm96-64H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,200,136Zm0,64H152V152h48v48Z" /> </svg> <span class="flex flex-col"> <span class="text-sm font-strong">Dashboards</span> <span class="text-sm text-muted-foreground">Your metrics, one page.</span> </span> </a> <a href="#" data-slot="navigation-menu-link" class="flex-row items-start gap-sm"> <svg class="size-md text-muted-foreground shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M216,112v96a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V112A16,16,0,0,1,56,96H80a8,8,0,0,1,0,16H56v96H200V112H176a8,8,0,0,1,0-16h24A16,16,0,0,1,216,112ZM93.66,69.66,120,43.31V136a8,8,0,0,0,16,0V43.31l26.34,26.35a8,8,0,0,0,11.32-11.32l-40-40a8,8,0,0,0-11.32,0l-40,40A8,8,0,0,0,93.66,69.66Z" /> </svg> <span class="flex flex-col"> <span class="text-sm font-strong">Exports</span> <span class="text-sm text-muted-foreground">Take your data anywhere.</span> </span> </a> <a href="#" data-slot="navigation-menu-link" class="flex-row items-start gap-sm"> <svg class="size-md text-muted-foreground shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M117.25,157.92a60,60,0,1,0-66.5,0A95.83,95.83,0,0,0,3.53,195.63a8,8,0,1,0,13.4,8.74,80,80,0,0,1,134.14,0,8,8,0,0,0,13.4-8.74A95.83,95.83,0,0,0,117.25,157.92ZM40,108a44,44,0,1,1,44,44A44.05,44.05,0,0,1,40,108Zm210.14,98.7a8,8,0,0,1-11.07-2.33A79.83,79.83,0,0,0,172,168a8,8,0,0,1,0-16,44,44,0,1,0-16.34-84.87,8,8,0,1,1-5.94-14.85,60,60,0,0,1,55.53,105.64,95.83,95.83,0,0,1,47.22,37.71A8,8,0,0,1,250.14,206.7Z" /> </svg> <span class="flex flex-col"> <span class="text-sm font-strong">Teams</span> <span class="text-sm text-muted-foreground">Roles and permissions.</span> </span> </a> <a href="#" data-slot="navigation-menu-link" class="flex-row items-start gap-sm"> <svg class="size-md text-muted-foreground shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M149.66,138.34a8,8,0,0,0-11.32,0L120,156.69,99.31,136l18.35-18.34a8,8,0,0,0-11.32-11.32L88,124.69,69.66,106.34a8,8,0,0,0-11.32,11.32L64.69,124,41.37,147.31a32,32,0,0,0,0,45.26l5.38,5.37-28.41,28.4a8,8,0,0,0,11.32,11.32l28.4-28.41,5.37,5.38a32,32,0,0,0,45.26,0L132,191.31l6.34,6.35a8,8,0,0,0,11.32-11.32L131.31,168l18.35-18.34A8,8,0,0,0,149.66,138.34Zm-52.29,65a16,16,0,0,1-22.62,0L52.69,181.25a16,16,0,0,1,0-22.62L76,135.31,120.69,180Zm140.29-185a8,8,0,0,0-11.32,0l-28.4,28.41-5.37-5.38a32.05,32.05,0,0,0-45.26,0L124,64.69l-6.34-6.35a8,8,0,0,0-11.32,11.32l80,80a8,8,0,0,0,11.32-11.32L191.31,132l23.32-23.31a32,32,0,0,0,0-45.26l-5.38-5.37,28.41-28.4A8,8,0,0,0,237.66,18.34Zm-34.35,79L180,120.69,135.31,76l23.32-23.31a16,16,0,0,1,22.62,0l22.06,22A16,16,0,0,1,203.31,97.37Z" /> </svg> <span class="flex flex-col"> <span class="text-sm font-strong">API</span> <span class="text-sm text-muted-foreground">Automate everything.</span> </span> </a> </div> </div> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">Docs</a> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">Pricing</a> </li> </menu> </nav></header>/** * navigation-menu.css: Navigation menu (.ui-navigation-menu) * * @layer variables, components * @requires layers.css, _variables.css, popover.css, button.css * @uses [popover] + nested popovers: megamenu panels and flyout submenus * @uses anchor-name, anchor-scope, position-anchor, anchor-size(): panel * sizing and positioning (Chromium 125+ for anchor-size) * @uses :has(:popover-open): chevron rotation without JS * @uses view-transition-name: stable header across navigations * @uses @starting-style: slide-down viewport reveal * @tokens --ui-navigation-menu-* (@layer variables) */@layer variables { :root { --ui-navigation-menu-icon-transition: transform var(--default-transition-duration) var(--default-transition-timing-function); --ui-navigation-menu-icon-transform: rotate(-180deg); --ui-navigation-menu-popover-shadow: var(--shadow-md); --ui-navigation-menu-popover-child-radius: var(--radius-sm); --ui-navigation-menu-popover-gap: var(--step-1_5); --ui-navigation-menu-popover-inline-size: min(var(--breakpoint-xs), 100% - var(--gap-md)); --ui-navigation-menu-popover-viewport-display: grid; --ui-navigation-menu-popover-viewport-align-items: stretch; --ui-navigation-menu-popover-viewport-justify-items: stretch; --ui-navigation-menu-popover-viewport-template-columns: 1fr; --ui-navigation-menu-popover-viewport-gap: var(--step-1_5); --ui-navigation-menu-popover-viewport-min-inline-size: var(--step-52); }}@layer zazz.components { .ui-navigation-menu { /* View transitions - keeps the header stable when navigating between pages */ --ui-popover-shadow: var(--ui-navigation-menu-popover-shadow); --ui-popover-child-radius: var(--ui-navigation-menu-popover-child-radius); --ui-popover-gap: var(--ui-navigation-menu-popover-gap); --ui-popover-inline-size: var(--ui-navigation-menu-popover-inline-size); --ui-popover-viewport-display: var(--ui-navigation-menu-popover-viewport-display); --ui-popover-viewport-align-items: var(--ui-navigation-menu-popover-viewport-align-items); --ui-popover-viewport-justify-items: var(--ui-navigation-menu-popover-viewport-justify-items); --ui-popover-viewport-template-columns: var( --ui-navigation-menu-popover-viewport-template-columns ); --ui-popover-viewport-gap: var(--ui-navigation-menu-popover-viewport-gap); --ui-popover-viewport-min-inline-size: var( --ui-navigation-menu-popover-viewport-min-inline-size ); display: inline-flex; position: relative; anchor-name: --ui-navigation-menu-menu-root; isolation: isolate; } [data-slot~="navigation-menu-list"] { display: flex; flex-direction: row; align-items: center; gap: var(--ui-popover-gap); anchor-name: --ui-navigation-menu-menu-list; } /* Each item scopes its own anchor name so triggers can share one name */ [data-slot~="navigation-menu-item"] { anchor-scope: --ui-navigation-menu-menu-trigger; position: relative; } [data-slot~="navigation-menu-trigger"] { anchor-name: --ui-navigation-menu-menu-trigger; } /* Chevron points down by default, flips up while its popover is open (no JS) */ [data-slot~="navigation-menu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); transition: var(--ui-navigation-menu-icon-transition); } [data-slot~="navigation-menu-item"]:has( [data-slot~="navigation-menu-popover"]:where(:popover-open, .\:popover-open) ) [data-slot~="navigation-menu-trigger"] > svg { transform: var(--ui-navigation-menu-icon-transform); } /* =========================================================================== POPOVER — inherits --ui-popover-* from popover.css; panel layout overrides here =========================================================================== */ [data-slot~="navigation-menu-popover"] { position-anchor: --ui-navigation-menu-menu-trigger; max-inline-size: var(--ui-popover-inline-size); --ui-popover-padding: 0; --ui-popover-min-inline-size: auto; /* Default: open below the trigger, aligned to its start edge */ --ui-popover-position-area: block-end span-inline-end; --ui-popover-origin: top center; --ui-popover-margin-block: var(--step-1_5); } [data-slot~="navigation-menu-popover"] .ui-button { --ui-button-radius: var(--radius-xs); } /* =========================================================================== ALIGNMENT — nav menus only open downward, so they set placement directly here rather than through data-side; popover.css's shared matrix keys on [data-side] and so never applies to these panels. =========================================================================== */ [data-slot~="navigation-menu-popover"][data-align="center"] { --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; } [data-slot~="navigation-menu-popover"][data-align="end"] { --ui-popover-position-area: block-end span-inline-start; --ui-popover-origin: top right; } /* =========================================================================== SUBMENU — categories inside a panel whose product lists fly out to the right. Native nested popovers: each sub-trigger's popovertarget lives inside the parent panel, so opening a submenu keeps the panel open (ancestor chain, no JS). =========================================================================== */ /* Each category scopes its own anchor so every sub-trigger reuses one name */ [data-slot~="navigation-menu-submenu"] { anchor-scope: --ui-navigation-menu-menu-subtrigger; position: relative; } /* Category row — a full-width link row with a trailing caret */ [data-slot~="navigation-menu-submenu-trigger"] { anchor-name: --ui-navigation-menu-menu-subtrigger; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-sm); inline-size: 100%; padding: var(--ui-popover-gap); border: 0; border-radius: var(--ui-popover-child-radius); color: inherit; font: inherit; text-align: start; cursor: pointer; } /* Highlight the row on hover and while its submenu is open */ [data-slot~="navigation-menu-submenu-trigger"]:hover, [data-slot~="navigation-menu-submenu"]:has( [data-slot~="navigation-menu-popover"][data-variant="submenu"]:where( :popover-open, .\:popover-open ) ) [data-slot~="navigation-menu-submenu-trigger"] { background-color: var(--muted); } /* Caret points right — the direction the submenu opens */ [data-slot~="navigation-menu-submenu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); flex: 0 0 auto; color: var(--muted-foreground); } /* Submenu (data-variant="submenu") inherits all base [data-slot~="navigation-menu-popover"] styling; only the anchor and open direction differ — it flies out to the inline-end edge, top-aligned, flipping leftward near the viewport's edge. */ [data-slot~="navigation-menu-popover"][data-variant="submenu"] { position-anchor: --ui-navigation-menu-menu-subtrigger; --ui-popover-position-area: inline-end span-block-start; --ui-popover-origin: left top; --ui-popover-margin-block: 0; --ui-popover-margin-inline: var(--step-1_5); --ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block; } /* =========================================================================== PANEL CONTENT =========================================================================== */ [data-slot~="navigation-menu-viewport"] { display: var(--ui-popover-viewport-display); grid-template-columns: var(--ui-popover-viewport-template-columns); align-items: var(--ui-popover-viewport-align-items); justify-items: var(--ui-popover-viewport-justify-items); gap: var(--ui-popover-viewport-gap); min-inline-size: var(--ui-popover-viewport-min-inline-size); } /* Rich link row: title + description, hover fills with muted */ [data-slot~="navigation-menu-link"] { display: flex; flex-direction: column; padding: var(--ui-popover-gap); border-radius: var(--ui-popover-child-radius); text-decoration: none; transition: background-color var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-link"]:hover { background-color: var(--muted); } /* =========================================================================== MEGAMENU ANIMATION =========================================================================== */ /* Slide-down: accordion-style block-size reveal on the viewport (not scaleY). */ [data-slot~="navigation-menu-popover"][data-size="screen"][data-animation="slide-down"] { --ui-popover-scale--start: 1; } [data-slot~="navigation-menu-popover"][data-animation="slide-down"] > [data-slot~="navigation-menu-viewport"] { overflow: clip; block-size: 0; transform: translateY(0.5rem); transform-origin: top center; transition: block-size var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function), padding var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) { opacity: var(--ui-popover-opacity--end); transform: scale(var(--ui-popover-scale--end)) translate3d(var(--ui-popover-translate--end)); @starting-style { opacity: var(--ui-popover-opacity--start); transform: scale(var(--ui-popover-scale--start)) translate3d(var(--ui-popover-translate--start)); } } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) [data-slot~="navigation-menu-viewport"] { block-size: auto; transform: scale(1) translateY(0); @starting-style { block-size: 0; transform: translateY(0.5rem); } } /* =========================================================================== SIZING VARIANTS =========================================================================== */ [data-slot~="navigation-menu-popover"][data-size="container"] { position-anchor: --ui-navigation-menu-menu-list; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="root"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="screen"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: 100vi; --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); }}Megamenu
A full-width panel using data-size="container" to size the popover to the whole menu bar via anchor-size(), holding columns of link groups with eyebrow labels.
<!-- Full-width megamenu: data-size="container" spans the menu bar; the panel is a 4-column grid of eyebrow-labeled link groups. --><header class="w-full"> <nav class="ui-navigation-menu container"> <menu data-slot="navigation-menu-list"> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-megamenu-1" > Shop <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-megamenu-1" data-slot="navigation-menu-popover" popover="auto" data-size="container" data-animation="slide-down" > <div class="grid grid-cols-4 gap-sm p-xs" data-slot="navigation-menu-viewport"> <div class="grid grid-rows-subgrid row-span-5 gap-px"> <span class="text-eyebrow text-muted-foreground pt-xs pb-xs">Dresses</span> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Mini & Midi</span> <span class="text-sm text-muted-foreground" >Everyday lengths for work and weekends.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Maxi Dresses</span> <span class="text-sm text-muted-foreground" >Flowing silhouettes in linen and cotton.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Jumpsuits</span> <span class="text-sm text-muted-foreground">One-piece outfits, easy to style.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Shop all dresses</span> <span class="text-sm text-muted-foreground">Browse the full collection.</span> </a> </div> <div class="grid grid-rows-subgrid row-span-5 gap-px"> <span class="text-eyebrow text-muted-foreground pt-xs pb-xs">Tops</span> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">T-shirts & Tanks</span> <span class="text-sm text-muted-foreground">Basics in organic cotton blends.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Blouses & Shirts</span> <span class="text-sm text-muted-foreground">Office-ready and off-duty styles.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Sweaters & Cardigans</span> <span class="text-sm text-muted-foreground">Layering pieces for cooler days.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Activewear</span> <span class="text-sm text-muted-foreground">Leggings, sports bras, and sets.</span> </a> </div> <div class="grid grid-rows-subgrid row-span-5 gap-px"> <span class="text-eyebrow text-muted-foreground pt-xs pb-xs">Shirts</span> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">T-shirts & Polos</span> <span class="text-sm text-muted-foreground" >Classic fits in breathable fabrics.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Oxford & Dress Shirts</span> <span class="text-sm text-muted-foreground" >Collared styles for work and events.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Henleys & Sweaters</span> <span class="text-sm text-muted-foreground" >Casual layers for transitional weather.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Shop all shirts</span> <span class="text-sm text-muted-foreground">See every cut and color.</span> </a> </div> <div class="grid grid-rows-subgrid row-span-5 gap-px"> <span class="text-eyebrow text-muted-foreground pt-xs pb-xs">Bedding</span> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Sheet Sets</span> <span class="text-sm text-muted-foreground" >Percale, sateen, and linen weaves.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Duvets & Comforters</span> <span class="text-sm text-muted-foreground" >All-season weights and down alternatives.</span > </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Pillows & Throws</span> <span class="text-sm text-muted-foreground">Accent layers for beds and sofas.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Shop all bedding</span> <span class="text-sm text-muted-foreground">Build a complete sleep setup.</span> </a> </div> </div> </div> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">New arrivals</a> </li> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">Sale</a> </li> </menu> </nav></header>/** * navigation-menu.css: Navigation menu (.ui-navigation-menu) * * @layer variables, components * @requires layers.css, _variables.css, popover.css, button.css * @uses [popover] + nested popovers: megamenu panels and flyout submenus * @uses anchor-name, anchor-scope, position-anchor, anchor-size(): panel * sizing and positioning (Chromium 125+ for anchor-size) * @uses :has(:popover-open): chevron rotation without JS * @uses view-transition-name: stable header across navigations * @uses @starting-style: slide-down viewport reveal * @tokens --ui-navigation-menu-* (@layer variables) */@layer variables { :root { --ui-navigation-menu-icon-transition: transform var(--default-transition-duration) var(--default-transition-timing-function); --ui-navigation-menu-icon-transform: rotate(-180deg); --ui-navigation-menu-popover-shadow: var(--shadow-md); --ui-navigation-menu-popover-child-radius: var(--radius-sm); --ui-navigation-menu-popover-gap: var(--step-1_5); --ui-navigation-menu-popover-inline-size: min(var(--breakpoint-xs), 100% - var(--gap-md)); --ui-navigation-menu-popover-viewport-display: grid; --ui-navigation-menu-popover-viewport-align-items: stretch; --ui-navigation-menu-popover-viewport-justify-items: stretch; --ui-navigation-menu-popover-viewport-template-columns: 1fr; --ui-navigation-menu-popover-viewport-gap: var(--step-1_5); --ui-navigation-menu-popover-viewport-min-inline-size: var(--step-52); }}@layer zazz.components { .ui-navigation-menu { /* View transitions - keeps the header stable when navigating between pages */ --ui-popover-shadow: var(--ui-navigation-menu-popover-shadow); --ui-popover-child-radius: var(--ui-navigation-menu-popover-child-radius); --ui-popover-gap: var(--ui-navigation-menu-popover-gap); --ui-popover-inline-size: var(--ui-navigation-menu-popover-inline-size); --ui-popover-viewport-display: var(--ui-navigation-menu-popover-viewport-display); --ui-popover-viewport-align-items: var(--ui-navigation-menu-popover-viewport-align-items); --ui-popover-viewport-justify-items: var(--ui-navigation-menu-popover-viewport-justify-items); --ui-popover-viewport-template-columns: var( --ui-navigation-menu-popover-viewport-template-columns ); --ui-popover-viewport-gap: var(--ui-navigation-menu-popover-viewport-gap); --ui-popover-viewport-min-inline-size: var( --ui-navigation-menu-popover-viewport-min-inline-size ); display: inline-flex; position: relative; anchor-name: --ui-navigation-menu-menu-root; isolation: isolate; } [data-slot~="navigation-menu-list"] { display: flex; flex-direction: row; align-items: center; gap: var(--ui-popover-gap); anchor-name: --ui-navigation-menu-menu-list; } /* Each item scopes its own anchor name so triggers can share one name */ [data-slot~="navigation-menu-item"] { anchor-scope: --ui-navigation-menu-menu-trigger; position: relative; } [data-slot~="navigation-menu-trigger"] { anchor-name: --ui-navigation-menu-menu-trigger; } /* Chevron points down by default, flips up while its popover is open (no JS) */ [data-slot~="navigation-menu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); transition: var(--ui-navigation-menu-icon-transition); } [data-slot~="navigation-menu-item"]:has( [data-slot~="navigation-menu-popover"]:where(:popover-open, .\:popover-open) ) [data-slot~="navigation-menu-trigger"] > svg { transform: var(--ui-navigation-menu-icon-transform); } /* =========================================================================== POPOVER — inherits --ui-popover-* from popover.css; panel layout overrides here =========================================================================== */ [data-slot~="navigation-menu-popover"] { position-anchor: --ui-navigation-menu-menu-trigger; max-inline-size: var(--ui-popover-inline-size); --ui-popover-padding: 0; --ui-popover-min-inline-size: auto; /* Default: open below the trigger, aligned to its start edge */ --ui-popover-position-area: block-end span-inline-end; --ui-popover-origin: top center; --ui-popover-margin-block: var(--step-1_5); } [data-slot~="navigation-menu-popover"] .ui-button { --ui-button-radius: var(--radius-xs); } /* =========================================================================== ALIGNMENT — nav menus only open downward, so they set placement directly here rather than through data-side; popover.css's shared matrix keys on [data-side] and so never applies to these panels. =========================================================================== */ [data-slot~="navigation-menu-popover"][data-align="center"] { --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; } [data-slot~="navigation-menu-popover"][data-align="end"] { --ui-popover-position-area: block-end span-inline-start; --ui-popover-origin: top right; } /* =========================================================================== SUBMENU — categories inside a panel whose product lists fly out to the right. Native nested popovers: each sub-trigger's popovertarget lives inside the parent panel, so opening a submenu keeps the panel open (ancestor chain, no JS). =========================================================================== */ /* Each category scopes its own anchor so every sub-trigger reuses one name */ [data-slot~="navigation-menu-submenu"] { anchor-scope: --ui-navigation-menu-menu-subtrigger; position: relative; } /* Category row — a full-width link row with a trailing caret */ [data-slot~="navigation-menu-submenu-trigger"] { anchor-name: --ui-navigation-menu-menu-subtrigger; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-sm); inline-size: 100%; padding: var(--ui-popover-gap); border: 0; border-radius: var(--ui-popover-child-radius); color: inherit; font: inherit; text-align: start; cursor: pointer; } /* Highlight the row on hover and while its submenu is open */ [data-slot~="navigation-menu-submenu-trigger"]:hover, [data-slot~="navigation-menu-submenu"]:has( [data-slot~="navigation-menu-popover"][data-variant="submenu"]:where( :popover-open, .\:popover-open ) ) [data-slot~="navigation-menu-submenu-trigger"] { background-color: var(--muted); } /* Caret points right — the direction the submenu opens */ [data-slot~="navigation-menu-submenu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); flex: 0 0 auto; color: var(--muted-foreground); } /* Submenu (data-variant="submenu") inherits all base [data-slot~="navigation-menu-popover"] styling; only the anchor and open direction differ — it flies out to the inline-end edge, top-aligned, flipping leftward near the viewport's edge. */ [data-slot~="navigation-menu-popover"][data-variant="submenu"] { position-anchor: --ui-navigation-menu-menu-subtrigger; --ui-popover-position-area: inline-end span-block-start; --ui-popover-origin: left top; --ui-popover-margin-block: 0; --ui-popover-margin-inline: var(--step-1_5); --ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block; } /* =========================================================================== PANEL CONTENT =========================================================================== */ [data-slot~="navigation-menu-viewport"] { display: var(--ui-popover-viewport-display); grid-template-columns: var(--ui-popover-viewport-template-columns); align-items: var(--ui-popover-viewport-align-items); justify-items: var(--ui-popover-viewport-justify-items); gap: var(--ui-popover-viewport-gap); min-inline-size: var(--ui-popover-viewport-min-inline-size); } /* Rich link row: title + description, hover fills with muted */ [data-slot~="navigation-menu-link"] { display: flex; flex-direction: column; padding: var(--ui-popover-gap); border-radius: var(--ui-popover-child-radius); text-decoration: none; transition: background-color var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-link"]:hover { background-color: var(--muted); } /* =========================================================================== MEGAMENU ANIMATION =========================================================================== */ /* Slide-down: accordion-style block-size reveal on the viewport (not scaleY). */ [data-slot~="navigation-menu-popover"][data-size="screen"][data-animation="slide-down"] { --ui-popover-scale--start: 1; } [data-slot~="navigation-menu-popover"][data-animation="slide-down"] > [data-slot~="navigation-menu-viewport"] { overflow: clip; block-size: 0; transform: translateY(0.5rem); transform-origin: top center; transition: block-size var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function), padding var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) { opacity: var(--ui-popover-opacity--end); transform: scale(var(--ui-popover-scale--end)) translate3d(var(--ui-popover-translate--end)); @starting-style { opacity: var(--ui-popover-opacity--start); transform: scale(var(--ui-popover-scale--start)) translate3d(var(--ui-popover-translate--start)); } } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) [data-slot~="navigation-menu-viewport"] { block-size: auto; transform: scale(1) translateY(0); @starting-style { block-size: 0; transform: translateY(0.5rem); } } /* =========================================================================== SIZING VARIANTS =========================================================================== */ [data-slot~="navigation-menu-popover"][data-size="container"] { position-anchor: --ui-navigation-menu-menu-list; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="root"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="screen"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: 100vi; --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); }}Simple menus and search
Rule of thumb: use a Menu when a nav item is a flat list of links, and a navigation-menu panel when it requires grid or multi-column layout. A <ui-menu> works as a nav item (reset --ui-popover-inline-size on its panel so wide navigation sizing does not inherit into it), and a .ui-input[type="search"] fits at the end of the bar. For full site search palettes, see Command.
<!-- A mixed bar: plain links, one rich panel, a plain link-list as a literal ui-menu item ("menu for a list of links; navigation-menu when a panel has layout"), and a search input at the end. --><header class="w-full"> <nav class="ui-navigation-menu container"> <menu data-slot="navigation-menu-list"> <li data-slot="navigation-menu-item"> <a href="#" class="ui-button" data-variant="ghost">Home</a> </li> <li data-slot="navigation-menu-item"> <button class="ui-button" data-slot="navigation-menu-trigger" data-variant="ghost" type="button" popovertarget="nav-simple-1" > Products <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> <div id="nav-simple-1" data-slot="navigation-menu-popover" data-align="start" popover="auto" data-animation="slide-down" > <div data-slot="navigation-menu-viewport"> <div class="flex flex-col gap-px p-xs"> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Editor</span> <span class="text-sm text-muted-foreground">Write and publish faster.</span> </a> <a href="#" data-slot="navigation-menu-link"> <span class="text-sm font-strong">Library</span> <span class="text-sm text-muted-foreground">Everything in one place.</span> </a> </div> </div> </div> </li> <!-- A plain link-list dropdown is just a menu primitive used as an item. The inline token reset keeps the nav's wide panel sizing from inheriting into the small menu panel. --> <li data-slot="navigation-menu-item"> <ui-menu> <button class="ui-button" data-variant="ghost" type="button" popovertarget="nav-simple-menu" interestfor="nav-simple-menu" > Company </button> <div id="nav-simple-menu" data-slot="menu-popover" data-side="bottom" data-align="start" popover="auto" style="--ui-popover-inline-size: max-content" > <menu> <li><a href="#" class="ui-button justify-start" data-variant="ghost">About</a></li> <li><a href="#" class="ui-button justify-start" data-variant="ghost">Careers</a></li> <li><a href="#" class="ui-button justify-start" data-variant="ghost">Press</a></li> </menu> </div> </ui-menu> </li> <li data-slot="navigation-menu-item" class="ml-auto"> <input class="ui-input" type="search" aria-label="Search the site" placeholder="Search…" style="max-inline-size: 16ch" /> </li> </menu> </nav></header>// primitives/menu/menu.js"use strict";/** * @fileoverview `<ui-menu>`: HTML web component for keyboard-enhanced menus. * @description Light-DOM custom element that augments the CSS-only menu * pattern (trigger + anchored `[data-slot~="menu-popover"]` panel) with * arrow-key navigation. The class form `.ui-menu` stays fully functional * without JavaScript: the Popover API provides open/close, light dismiss, * and focus return on its own. * * Keyboard behavior: * - ArrowDown / ArrowUp on the closed trigger open the panel and focus the * first / last item. * - ArrowDown / ArrowUp inside the open panel move focus between items, * wrapping around and skipping disabled items. * - Home / End jump to the first / last item. * - Escape and light dismiss are native Popover API behavior (no code here). * * The menu keeps the honest disclosure posture: items are plain links and * buttons, and no `role="menu"` is claimed. Add the full ARIA menu contract * yourself only if every item is an action and you implement the rest of the * pattern (typeahead, close-on-activate). * * @example * <ui-menu> * <button class="ui-button" type="button" popovertarget="m1">Open</button> * <div id="m1" data-slot="menu-popover" popover="auto"> * <menu> * <li><a href="/docs" class="ui-button justify-start" data-variant="ghost">Docs</a></li> * </menu> * </div> * </ui-menu> */import { ZazzElement, defineZazzElement } from "../../base/zazz-element.js";class UiMenu extends ZazzElement { setup(signal) { this.addEventListener("keydown", (event) => this.#onKeydown(event), { signal }); } /** * @description The menu's own panel: a direct child so nested menus keep * their panels to themselves. * * @returns The panel element, or null when the markup is incomplete. */ #panel() { const panel = this.querySelector(':scope > [data-slot~="menu-popover"]'); return panel instanceof HTMLElement ? panel : null; } /** * @description Focusable items inside the panel, in DOM order. * * @param panel - The menu panel. * @returns Enabled links and buttons the arrow keys move between. */ #items(panel) { return Array.from(panel.querySelectorAll("a[href], button")) .filter((node) => node instanceof HTMLElement) .filter((item) => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true" && item.closest("ui-menu, .ui-menu") === this); } /** * @description Routes arrow-key, Home, and End presses: opens the panel from * the trigger, or moves focus between items inside the open panel. * * @param event - The keydown event. */ #onKeydown(event) { if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return; const target = event.target; if (!(target instanceof HTMLElement)) return; // Ignore keys that belong to a nested ui-menu if (target.closest("ui-menu") !== this) return; const panel = this.#panel(); if (!panel) return; const isTrigger = target.parentElement === this && (target.hasAttribute("popovertarget") || target.hasAttribute("interestfor")); const open = panel.matches(":popover-open, .\\:popover-open"); if (isTrigger && !open && (event.key === "ArrowDown" || event.key === "ArrowUp")) { event.preventDefault(); panel.showPopover(); const items = this.#items(panel); items[event.key === "ArrowDown" ? 0 : items.length - 1]?.focus(); return; } if (!open || !panel.contains(target)) return; const items = this.#items(panel); if (items.length === 0) return; const index = items.indexOf(target); let nextIndex; switch (event.key) { case "ArrowDown": nextIndex = index === -1 ? 0 : (index + 1) % items.length; break; case "ArrowUp": nextIndex = index === -1 ? items.length - 1 : (index - 1 + items.length) % items.length; break; case "Home": nextIndex = 0; break; case "End": nextIndex = items.length - 1; break; default: return; } event.preventDefault(); items[nextIndex].focus(); }}defineZazzElement("ui-menu", UiMenu);export { UiMenu };/** * navigation-menu.css: Navigation menu (.ui-navigation-menu) * * @layer variables, components * @requires layers.css, _variables.css, popover.css, button.css * @uses [popover] + nested popovers: megamenu panels and flyout submenus * @uses anchor-name, anchor-scope, position-anchor, anchor-size(): panel * sizing and positioning (Chromium 125+ for anchor-size) * @uses :has(:popover-open): chevron rotation without JS * @uses view-transition-name: stable header across navigations * @uses @starting-style: slide-down viewport reveal * @tokens --ui-navigation-menu-* (@layer variables) */@layer variables { :root { --ui-navigation-menu-icon-transition: transform var(--default-transition-duration) var(--default-transition-timing-function); --ui-navigation-menu-icon-transform: rotate(-180deg); --ui-navigation-menu-popover-shadow: var(--shadow-md); --ui-navigation-menu-popover-child-radius: var(--radius-sm); --ui-navigation-menu-popover-gap: var(--step-1_5); --ui-navigation-menu-popover-inline-size: min(var(--breakpoint-xs), 100% - var(--gap-md)); --ui-navigation-menu-popover-viewport-display: grid; --ui-navigation-menu-popover-viewport-align-items: stretch; --ui-navigation-menu-popover-viewport-justify-items: stretch; --ui-navigation-menu-popover-viewport-template-columns: 1fr; --ui-navigation-menu-popover-viewport-gap: var(--step-1_5); --ui-navigation-menu-popover-viewport-min-inline-size: var(--step-52); }}@layer zazz.components { .ui-navigation-menu { /* View transitions - keeps the header stable when navigating between pages */ --ui-popover-shadow: var(--ui-navigation-menu-popover-shadow); --ui-popover-child-radius: var(--ui-navigation-menu-popover-child-radius); --ui-popover-gap: var(--ui-navigation-menu-popover-gap); --ui-popover-inline-size: var(--ui-navigation-menu-popover-inline-size); --ui-popover-viewport-display: var(--ui-navigation-menu-popover-viewport-display); --ui-popover-viewport-align-items: var(--ui-navigation-menu-popover-viewport-align-items); --ui-popover-viewport-justify-items: var(--ui-navigation-menu-popover-viewport-justify-items); --ui-popover-viewport-template-columns: var( --ui-navigation-menu-popover-viewport-template-columns ); --ui-popover-viewport-gap: var(--ui-navigation-menu-popover-viewport-gap); --ui-popover-viewport-min-inline-size: var( --ui-navigation-menu-popover-viewport-min-inline-size ); display: inline-flex; position: relative; anchor-name: --ui-navigation-menu-menu-root; isolation: isolate; } [data-slot~="navigation-menu-list"] { display: flex; flex-direction: row; align-items: center; gap: var(--ui-popover-gap); anchor-name: --ui-navigation-menu-menu-list; } /* Each item scopes its own anchor name so triggers can share one name */ [data-slot~="navigation-menu-item"] { anchor-scope: --ui-navigation-menu-menu-trigger; position: relative; } [data-slot~="navigation-menu-trigger"] { anchor-name: --ui-navigation-menu-menu-trigger; } /* Chevron points down by default, flips up while its popover is open (no JS) */ [data-slot~="navigation-menu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); transition: var(--ui-navigation-menu-icon-transition); } [data-slot~="navigation-menu-item"]:has( [data-slot~="navigation-menu-popover"]:where(:popover-open, .\:popover-open) ) [data-slot~="navigation-menu-trigger"] > svg { transform: var(--ui-navigation-menu-icon-transform); } /* =========================================================================== POPOVER — inherits --ui-popover-* from popover.css; panel layout overrides here =========================================================================== */ [data-slot~="navigation-menu-popover"] { position-anchor: --ui-navigation-menu-menu-trigger; max-inline-size: var(--ui-popover-inline-size); --ui-popover-padding: 0; --ui-popover-min-inline-size: auto; /* Default: open below the trigger, aligned to its start edge */ --ui-popover-position-area: block-end span-inline-end; --ui-popover-origin: top center; --ui-popover-margin-block: var(--step-1_5); } [data-slot~="navigation-menu-popover"] .ui-button { --ui-button-radius: var(--radius-xs); } /* =========================================================================== ALIGNMENT — nav menus only open downward, so they set placement directly here rather than through data-side; popover.css's shared matrix keys on [data-side] and so never applies to these panels. =========================================================================== */ [data-slot~="navigation-menu-popover"][data-align="center"] { --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; } [data-slot~="navigation-menu-popover"][data-align="end"] { --ui-popover-position-area: block-end span-inline-start; --ui-popover-origin: top right; } /* =========================================================================== SUBMENU — categories inside a panel whose product lists fly out to the right. Native nested popovers: each sub-trigger's popovertarget lives inside the parent panel, so opening a submenu keeps the panel open (ancestor chain, no JS). =========================================================================== */ /* Each category scopes its own anchor so every sub-trigger reuses one name */ [data-slot~="navigation-menu-submenu"] { anchor-scope: --ui-navigation-menu-menu-subtrigger; position: relative; } /* Category row — a full-width link row with a trailing caret */ [data-slot~="navigation-menu-submenu-trigger"] { anchor-name: --ui-navigation-menu-menu-subtrigger; display: flex; align-items: center; justify-content: space-between; gap: var(--gap-sm); inline-size: 100%; padding: var(--ui-popover-gap); border: 0; border-radius: var(--ui-popover-child-radius); color: inherit; font: inherit; text-align: start; cursor: pointer; } /* Highlight the row on hover and while its submenu is open */ [data-slot~="navigation-menu-submenu-trigger"]:hover, [data-slot~="navigation-menu-submenu"]:has( [data-slot~="navigation-menu-popover"][data-variant="submenu"]:where( :popover-open, .\:popover-open ) ) [data-slot~="navigation-menu-submenu-trigger"] { background-color: var(--muted); } /* Caret points right — the direction the submenu opens */ [data-slot~="navigation-menu-submenu-trigger"] > svg { inline-size: var(--gap-sm); block-size: var(--gap-sm); flex: 0 0 auto; color: var(--muted-foreground); } /* Submenu (data-variant="submenu") inherits all base [data-slot~="navigation-menu-popover"] styling; only the anchor and open direction differ — it flies out to the inline-end edge, top-aligned, flipping leftward near the viewport's edge. */ [data-slot~="navigation-menu-popover"][data-variant="submenu"] { position-anchor: --ui-navigation-menu-menu-subtrigger; --ui-popover-position-area: inline-end span-block-start; --ui-popover-origin: left top; --ui-popover-margin-block: 0; --ui-popover-margin-inline: var(--step-1_5); --ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block; } /* =========================================================================== PANEL CONTENT =========================================================================== */ [data-slot~="navigation-menu-viewport"] { display: var(--ui-popover-viewport-display); grid-template-columns: var(--ui-popover-viewport-template-columns); align-items: var(--ui-popover-viewport-align-items); justify-items: var(--ui-popover-viewport-justify-items); gap: var(--ui-popover-viewport-gap); min-inline-size: var(--ui-popover-viewport-min-inline-size); } /* Rich link row: title + description, hover fills with muted */ [data-slot~="navigation-menu-link"] { display: flex; flex-direction: column; padding: var(--ui-popover-gap); border-radius: var(--ui-popover-child-radius); text-decoration: none; transition: background-color var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-link"]:hover { background-color: var(--muted); } /* =========================================================================== MEGAMENU ANIMATION =========================================================================== */ /* Slide-down: accordion-style block-size reveal on the viewport (not scaleY). */ [data-slot~="navigation-menu-popover"][data-size="screen"][data-animation="slide-down"] { --ui-popover-scale--start: 1; } [data-slot~="navigation-menu-popover"][data-animation="slide-down"] > [data-slot~="navigation-menu-viewport"] { overflow: clip; block-size: 0; transform: translateY(0.5rem); transform-origin: top center; transition: block-size var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function), padding var(--default-transition-duration) var(--default-transition-timing-function); } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) { opacity: var(--ui-popover-opacity--end); transform: scale(var(--ui-popover-scale--end)) translate3d(var(--ui-popover-translate--end)); @starting-style { opacity: var(--ui-popover-opacity--start); transform: scale(var(--ui-popover-scale--start)) translate3d(var(--ui-popover-translate--start)); } } [data-slot~="navigation-menu-popover"][data-animation="slide-down"]:where( :popover-open, .\:popover-open ) [data-slot~="navigation-menu-viewport"] { block-size: auto; transform: scale(1) translateY(0); @starting-style { block-size: 0; transform: translateY(0.5rem); } } /* =========================================================================== SIZING VARIANTS =========================================================================== */ [data-slot~="navigation-menu-popover"][data-size="container"] { position-anchor: --ui-navigation-menu-menu-list; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="root"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: anchor-size(); --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); } [data-slot~="navigation-menu-popover"][data-size="screen"] { position-anchor: --ui-navigation-menu-menu-root; --ui-popover-inline-size: 100vi; --ui-popover-position-area: block-end span-all; inline-size: var(--ui-popover-inline-size); }}API
| Attribute | Target | Values |
|---|---|---|
popovertarget | Trigger | ID of target panel |
interestfor | Trigger | Same ID: adds hover/focus open (optional) |
data-align | [data-slot="navigation-menu-popover"] | center, end |
data-variant | [data-slot="navigation-menu-popover"] | submenu |
data-size | [data-slot="navigation-menu-popover"] | root, container, screen |
data-animation | [data-slot="navigation-menu-popover"] | slide-down |