API
Navigation menu
Site navigation with link panels, flyouts, and megamenus.
Installation
pnpm dlx zazz-ui add navigation-menunpx zazz-ui add navigation-menuyarn dlx zazz-ui add navigation-menubunx zazz-ui add navigation-menuAlso adds fields, kbd, button, popover, which it depends on. Run zazz-ui init first if the project has no zazz/ folder yet.
Examples
Default
<header data-ui="layout" style="--w: 100%">
<nav data-ui="navigation-menu">
<menu data-navigation-menu-slot="list">
<!-- Rich panel: featured panel + link grid -->
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-panel-1"
>
Platform
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div
data-navigation-menu-slot="viewport"
style="
--display: grid;
--grid-cols: 3;
"
>
<div
style="
--display: grid;
--py: 2;
--pl: 2;
"
>
<a
href="#"
style="
--display: flex-col;
--items: start;
--justify: end;
--p: 4;
--bg: var(--color-muted);
--opacity: 1;
--opacity--hover: 0.75;
--transition: var(--default-transition);
--rounded: var(--radius-sm);
"
>
<span
style="
--display: inline-flex;
--mb: 2;
--font-size: var(--font-size-eyebrow);
--font-weight: var(--font-weight-eyebrow);
--leading: var(--leading-eyebrow);
--tracking: var(--tracking-eyebrow);
--text-transform: uppercase;
--text: var(--color-muted-foreground);
"
>Featured</span
>
<span
style="
--font-size: var(--font-size-md);
--font-weight: strong;
--leading: var(--leading-md);
--tracking: var(--tracking-md);
"
>Platform</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text-wrap: pretty;
--text: var(--color-muted-foreground);
"
>
Everything you need to design, build, and ship.
</span>
</a>
</div>
<div
style="
--display: flex-col;
--col-span: 2;
--py: 2;
--pr: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Analytics</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Real-time insights into product usage.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Automation</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Workflows that run themselves.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Integrations</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Connect the tools your team already uses.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Features</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Everything you need to design, build, and ship.</span
>
</a>
</div>
</div>
</div>
</li>
<!-- Simple panel: single-column link list -->
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-panel-2"
>
Resources
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
data-popover-align="start"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Documentation</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Guides and API references.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Blog</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Product news and engineering notes.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Community</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Join the conversation and get help.</span
>
</a>
</div>
</div>
</div>
</li>
<!-- Submenu panel -->
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-panel-3"
>
Products
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<!-- Category -->
<div data-navigation-menu-slot="submenu">
<button
data-navigation-menu-slot="submenu-trigger"
type="button"
popovertarget="nav-submenu-1"
>
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>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-navigation-menu-slot="popover"
data-navigation-menu-variant="submenu"
popover="auto"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Figma Kit</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Variables, styles, and components.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Color Generator</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>OKLCH scales from a single brand hex.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Type Scale</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Fluid sizes tuned with Utopia.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Icon Set</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>A consistent, lightweight icon library.</span
>
</a>
</div>
</div>
</div>
</div>
<!-- Category -->
<div data-navigation-menu-slot="submenu">
<button
data-navigation-menu-slot="submenu-trigger"
type="button"
popovertarget="nav-submenu-2"
>
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>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-navigation-menu-slot="popover"
data-navigation-menu-variant="submenu"
popover="auto"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>CLI</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Scaffold and ship from the terminal.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>React Components</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Token-driven primitives for your app.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>REST API</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Programmatic access to your design data.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Webhooks</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>React to changes in real time.</span
>
</a>
</div>
</div>
</div>
</div>
<!-- Category -->
<div data-navigation-menu-slot="submenu">
<button
data-navigation-menu-slot="submenu-trigger"
type="button"
popovertarget="nav-submenu-3"
>
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>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-navigation-menu-slot="popover"
data-navigation-menu-variant="submenu"
popover="auto"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>CMS</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Structured content for any surface.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Localization</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Ship in every language your users speak.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Media Library</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Optimized assets, delivered fast.</span
>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</li>
<li
data-navigation-menu-slot="item"
style="
--ml: auto;
--mr: 2;
"
>
<button
data-ui="button"
type="button"
data-button-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" data-ui="dialog" closedby="any">
<form method="dialog" data-dialog-slot="content">
<header data-dialog-slot="header">
<h2
style="
--font-size: var(--font-size-lg);
--font-weight: strong;
--font-family: var(--font-family-heading);
--leading: var(--leading-lg);
--tracking: var(--tracking-lg);
"
>
Sign in
</h2>
<p style="--text: var(--color-muted-foreground)">
Enter your email and password to sign in.
</p>
</header>
<div
data-dialog-slot="body"
style="
--display: flex-col;
--gap: 6;
"
>
<fieldset data-ui="field-group">
<legend data-ui="sr-only">Sign in</legend>
<div
style="
--display: grid;
--gap: 2;
"
>
<div data-ui="field">
<label data-field-slot="label" for="fd-email">Email</label>
<input
data-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-field-slot="description">
<span data-field-slot="hint" id="fd-email-hint"
>We'll only use this to send receipts.</span
>
<span data-field-slot="error" role="alert"
>Please enter a valid email address.</span
>
</div>
</div>
<div data-ui="field">
<label data-field-slot="label" for="f-login-password">Password</label>
<ui-password>
<label data-ui="password-group">
<input
data-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-password-group-slot="addon"
data-password-group-align="inline-end"
>
<button
data-ui="button"
data-password-group-slot="toggle"
id="f-login-password-toggle"
data-button-variant="ghost"
data-button-size="icon-sm"
type="button"
aria-pressed="false"
aria-label="Show password"
aria-describedby="f-login-password-warning"
>
<svg
data-password-group-slot="icon 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-password-group-slot="icon 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-field-slot="description">
<span data-field-slot="error" role="alert">Incorrect email or password.</span>
</div>
<span data-ui="sr-only" id="f-login-password-warning"
>Warning: showing the password makes it visible to anyone near your
screen.</span
>
</div>
</div>
</fieldset>
</div>
<footer data-dialog-slot="footer">
<button
data-ui="button"
type="button"
value="cancel"
commandfor="form-sign-in-1"
command="close"
>
Cancel
</button>
<button data-ui="button" data-button-variant="primary" type="submit" value="save">
Sign in
</button>
</footer>
</form>
<button
data-ui="button"
data-dialog-slot="close"
data-button-size="icon"
data-button-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>
</li>
<!-- Plain CTA — no popover -->
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="primary">Get started</a>
</li>
</menu>
</nav>
</header>/**
* navigation-menu.css: Navigation menu ([data-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): rotates the trigger's data-navigation-menu-slot="marker"
* chevron without JS
* @uses @starting-style: slide-down viewport reveal
* @tokens --ui-navigation-menu-* (@layer variables)
* @presets on the popover slot: data-navigation-menu-variant="submenu",
* data-navigation-menu-size="container | root | screen",
* data-navigation-menu-animation="slide-down"
*/
@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-rounded: var(--radius-sm);
--ui-navigation-menu-popover-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-w: min(var(--article-sm), 100% - var(--space-md));
--ui-navigation-menu-popover-viewport-display: grid;
--ui-navigation-menu-popover-viewport-items: stretch;
--ui-navigation-menu-popover-viewport-justify-items: stretch;
--ui-navigation-menu-popover-viewport-template-cols: 1fr;
--ui-navigation-menu-popover-viewport-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-viewport-min-w: calc(var(--spacing) * 52);
}
}
@layer ui.components {
:where([data-ui~="navigation-menu"]) {
--ui-popover-shadow: var(--ui-navigation-menu-popover-shadow);
display: inline-flex;
position: relative;
anchor-name: --ui-navigation-menu-menu-root;
isolation: isolate;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="list"] {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ui-navigation-menu-popover-gap);
anchor-name: --ui-navigation-menu-menu-list;
}
/* Per-item anchor scope lets every trigger share one name */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="item"] {
anchor-scope: --ui-navigation-menu-menu-trigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="trigger"] {
anchor-name: --ui-navigation-menu-menu-trigger;
}
/* Chevron points down by default, flips up while its popover is open (no JS) */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
transition: var(--ui-navigation-menu-icon-transition);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="item"]:has([data-navigation-menu-slot~="popover"]:popover-open)
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
transform: var(--ui-navigation-menu-icon-transform);
}
/* ===========================================================================
POPOVER
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] {
position-anchor: --ui-navigation-menu-menu-trigger;
max-inline-size: var(--ui-navigation-menu-popover-w);
--ui-popover-p: 0;
--ui-popover-min-w: 0;
--ui-popover-position-area: block-end span-inline-end;
--ui-popover-origin: top center;
--ui-popover-my: calc(var(--spacing) * 1.5);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] [data-ui~="button"] {
--ui-button-rounded: var(--radius-xs);
}
/* ===========================================================================
ALIGNMENT — panels always open downward and set placement directly;
popover.css's [data-popover-side] matrix never applies to them.
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="center"] {
--ui-popover-position-area: block-end span-all;
--ui-popover-origin: top center;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="end"] {
--ui-popover-position-area: block-end span-inline-start;
--ui-popover-origin: top right;
}
/* ===========================================================================
SUBMENU — nested native popovers: the sub-trigger lives inside the parent
panel, so opening a submenu keeps the panel open.
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu"] {
anchor-scope: --ui-navigation-menu-menu-subtrigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] {
anchor-name: --ui-navigation-menu-menu-subtrigger;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
inline-size: 100%;
padding: var(--ui-navigation-menu-popover-gap);
border: 0;
border-radius: var(--ui-navigation-menu-popover-child-rounded);
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="submenu"]:has(
[data-navigation-menu-slot~="popover"][data-navigation-menu-variant="submenu"]:popover-open
)
[data-navigation-menu-slot~="submenu-trigger"] {
background-color: var(--color-muted);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"]:hover {
background-color: var(--color-muted);
}
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] > svg {
inline-size: var(--space-sm);
block-size: var(--space-sm);
flex: 0 0 auto;
color: var(--color-muted-foreground);
}
/* Inherits the base popover styling; only anchor and direction differ:
opens at the inline-end edge, flipping when near the viewport edge. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-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-my: 0;
--ui-popover-mx: calc(var(--spacing) * 1.5);
--ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}
/* ===========================================================================
PANEL CONTENT
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="viewport"] {
display: var(--ui-navigation-menu-popover-viewport-display);
grid-template-columns: var(--ui-navigation-menu-popover-viewport-template-cols);
align-items: var(--ui-navigation-menu-popover-viewport-items);
justify-items: var(--ui-navigation-menu-popover-viewport-justify-items);
gap: var(--ui-navigation-menu-popover-viewport-gap);
min-inline-size: var(--ui-navigation-menu-popover-viewport-min-w);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"] {
display: flex;
flex-direction: column;
padding: var(--ui-navigation-menu-popover-gap);
border-radius: var(--ui-navigation-menu-popover-child-rounded);
text-decoration: none;
transition: background-color var(--default-transition-duration)
var(--default-transition-timing-function);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"]:hover {
background-color: var(--color-muted);
}
}
/* ===========================================================================
MEGAMENU ANIMATION
=========================================================================== */
/* Block-size reveal on the viewport, not scaleY. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"][data-navigation-menu-animation="slide-down"] {
--ui-popover-scale--start: 1;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]
> [data-navigation-menu-slot~="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);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]: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));
}
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]:popover-open
[data-navigation-menu-slot~="viewport"] {
block-size: auto;
transform: scale(1) translateY(0);
@starting-style {
block-size: 0;
transform: translateY(0.5rem);
}
}
/* ===========================================================================
SIZING VARIANTS
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="container"] {
position-anchor: --ui-navigation-menu-menu-list;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="root"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: 100vi;
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
}Simple
<!-- A mixed bar: plain links, one rich panel, a plain link-list as a ui-menu
item, and a search input at the end. -->
<header
data-ui="layout"
style="
--display: grid;
--w: 100%;
"
>
<nav data-ui="navigation-menu" style="--w: 100%">
<menu data-navigation-menu-slot="list" style="--w: 100%">
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">Home</a>
</li>
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-simple-1"
>
Products
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
data-popover-align="start"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Editor</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Write and publish faster.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Library</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Everything in one place.</span
>
</a>
</div>
</div>
</div>
</li>
<li data-navigation-menu-slot="item">
<ui-menu>
<button
data-ui="button"
data-button-variant="ghost"
type="button"
popovertarget="nav-simple-menu"
interestfor="nav-simple-menu"
>
Company
</button>
<div
id="nav-simple-menu"
data-menu-slot="popover"
data-popover-side="bottom"
data-popover-align="start"
popover="auto"
>
<menu>
<li>
<a href="#" data-ui="button" data-button-variant="ghost" style="--justify: start"
>About</a
>
</li>
<li>
<a href="#" data-ui="button" data-button-variant="ghost" style="--justify: start"
>Careers</a
>
</li>
<li>
<a href="#" data-ui="button" data-button-variant="ghost" style="--justify: start"
>Press</a
>
</li>
</menu>
</div>
</ui-menu>
</li>
<li data-navigation-menu-slot="item" style="--ml: auto">
<input
data-ui="input"
type="search"
name="q"
aria-label="Search the site"
placeholder="Search…"
style="--min-w: 40ch"
/>
</li>
</menu>
</nav>
</header>/**
* navigation-menu.css: Navigation menu ([data-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): rotates the trigger's data-navigation-menu-slot="marker"
* chevron without JS
* @uses @starting-style: slide-down viewport reveal
* @tokens --ui-navigation-menu-* (@layer variables)
* @presets on the popover slot: data-navigation-menu-variant="submenu",
* data-navigation-menu-size="container | root | screen",
* data-navigation-menu-animation="slide-down"
*/
@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-rounded: var(--radius-sm);
--ui-navigation-menu-popover-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-w: min(var(--article-sm), 100% - var(--space-md));
--ui-navigation-menu-popover-viewport-display: grid;
--ui-navigation-menu-popover-viewport-items: stretch;
--ui-navigation-menu-popover-viewport-justify-items: stretch;
--ui-navigation-menu-popover-viewport-template-cols: 1fr;
--ui-navigation-menu-popover-viewport-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-viewport-min-w: calc(var(--spacing) * 52);
}
}
@layer ui.components {
:where([data-ui~="navigation-menu"]) {
--ui-popover-shadow: var(--ui-navigation-menu-popover-shadow);
display: inline-flex;
position: relative;
anchor-name: --ui-navigation-menu-menu-root;
isolation: isolate;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="list"] {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ui-navigation-menu-popover-gap);
anchor-name: --ui-navigation-menu-menu-list;
}
/* Per-item anchor scope lets every trigger share one name */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="item"] {
anchor-scope: --ui-navigation-menu-menu-trigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="trigger"] {
anchor-name: --ui-navigation-menu-menu-trigger;
}
/* Chevron points down by default, flips up while its popover is open (no JS) */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
transition: var(--ui-navigation-menu-icon-transition);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="item"]:has([data-navigation-menu-slot~="popover"]:popover-open)
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
transform: var(--ui-navigation-menu-icon-transform);
}
/* ===========================================================================
POPOVER
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] {
position-anchor: --ui-navigation-menu-menu-trigger;
max-inline-size: var(--ui-navigation-menu-popover-w);
--ui-popover-p: 0;
--ui-popover-min-w: 0;
--ui-popover-position-area: block-end span-inline-end;
--ui-popover-origin: top center;
--ui-popover-my: calc(var(--spacing) * 1.5);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] [data-ui~="button"] {
--ui-button-rounded: var(--radius-xs);
}
/* ===========================================================================
ALIGNMENT — panels always open downward and set placement directly;
popover.css's [data-popover-side] matrix never applies to them.
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="center"] {
--ui-popover-position-area: block-end span-all;
--ui-popover-origin: top center;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="end"] {
--ui-popover-position-area: block-end span-inline-start;
--ui-popover-origin: top right;
}
/* ===========================================================================
SUBMENU — nested native popovers: the sub-trigger lives inside the parent
panel, so opening a submenu keeps the panel open.
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu"] {
anchor-scope: --ui-navigation-menu-menu-subtrigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] {
anchor-name: --ui-navigation-menu-menu-subtrigger;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
inline-size: 100%;
padding: var(--ui-navigation-menu-popover-gap);
border: 0;
border-radius: var(--ui-navigation-menu-popover-child-rounded);
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="submenu"]:has(
[data-navigation-menu-slot~="popover"][data-navigation-menu-variant="submenu"]:popover-open
)
[data-navigation-menu-slot~="submenu-trigger"] {
background-color: var(--color-muted);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"]:hover {
background-color: var(--color-muted);
}
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] > svg {
inline-size: var(--space-sm);
block-size: var(--space-sm);
flex: 0 0 auto;
color: var(--color-muted-foreground);
}
/* Inherits the base popover styling; only anchor and direction differ:
opens at the inline-end edge, flipping when near the viewport edge. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-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-my: 0;
--ui-popover-mx: calc(var(--spacing) * 1.5);
--ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}
/* ===========================================================================
PANEL CONTENT
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="viewport"] {
display: var(--ui-navigation-menu-popover-viewport-display);
grid-template-columns: var(--ui-navigation-menu-popover-viewport-template-cols);
align-items: var(--ui-navigation-menu-popover-viewport-items);
justify-items: var(--ui-navigation-menu-popover-viewport-justify-items);
gap: var(--ui-navigation-menu-popover-viewport-gap);
min-inline-size: var(--ui-navigation-menu-popover-viewport-min-w);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"] {
display: flex;
flex-direction: column;
padding: var(--ui-navigation-menu-popover-gap);
border-radius: var(--ui-navigation-menu-popover-child-rounded);
text-decoration: none;
transition: background-color var(--default-transition-duration)
var(--default-transition-timing-function);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"]:hover {
background-color: var(--color-muted);
}
}
/* ===========================================================================
MEGAMENU ANIMATION
=========================================================================== */
/* Block-size reveal on the viewport, not scaleY. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"][data-navigation-menu-animation="slide-down"] {
--ui-popover-scale--start: 1;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]
> [data-navigation-menu-slot~="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);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]: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));
}
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]:popover-open
[data-navigation-menu-slot~="viewport"] {
block-size: auto;
transform: scale(1) translateY(0);
@starting-style {
block-size: 0;
transform: translateY(0.5rem);
}
}
/* ===========================================================================
SIZING VARIANTS
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="container"] {
position-anchor: --ui-navigation-menu-menu-list;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="root"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: 100vi;
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
}Interest
<!-- Hover-open variant: every trigger carries `interestfor` alongside
`popovertarget` (same id), so panels open on hover/focus and on click. -->
<header data-ui="layout" style="--w: 100%">
<nav data-ui="navigation-menu">
<menu data-navigation-menu-slot="list">
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-interest-1"
interestfor="nav-interest-1"
>
Platform
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
data-popover-align="start"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Analytics</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Real-time product insights.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Automation</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Workflows that run themselves.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Integrations</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Connect your team's tools.</span
>
</a>
</div>
</div>
</div>
</li>
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-interest-2"
interestfor="nav-interest-2"
>
Resources
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
data-popover-align="start"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div data-navigation-menu-slot="viewport">
<div
style="
--display: flex-col;
--p: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Documentation</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Guides and API references.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Blog</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Product news and notes.</span
>
</a>
</div>
</div>
</div>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">Pricing</a>
</li>
</menu>
</nav>
</header>/**
* navigation-menu.css: Navigation menu ([data-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): rotates the trigger's data-navigation-menu-slot="marker"
* chevron without JS
* @uses @starting-style: slide-down viewport reveal
* @tokens --ui-navigation-menu-* (@layer variables)
* @presets on the popover slot: data-navigation-menu-variant="submenu",
* data-navigation-menu-size="container | root | screen",
* data-navigation-menu-animation="slide-down"
*/
@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-rounded: var(--radius-sm);
--ui-navigation-menu-popover-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-w: min(var(--article-sm), 100% - var(--space-md));
--ui-navigation-menu-popover-viewport-display: grid;
--ui-navigation-menu-popover-viewport-items: stretch;
--ui-navigation-menu-popover-viewport-justify-items: stretch;
--ui-navigation-menu-popover-viewport-template-cols: 1fr;
--ui-navigation-menu-popover-viewport-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-viewport-min-w: calc(var(--spacing) * 52);
}
}
@layer ui.components {
:where([data-ui~="navigation-menu"]) {
--ui-popover-shadow: var(--ui-navigation-menu-popover-shadow);
display: inline-flex;
position: relative;
anchor-name: --ui-navigation-menu-menu-root;
isolation: isolate;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="list"] {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ui-navigation-menu-popover-gap);
anchor-name: --ui-navigation-menu-menu-list;
}
/* Per-item anchor scope lets every trigger share one name */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="item"] {
anchor-scope: --ui-navigation-menu-menu-trigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="trigger"] {
anchor-name: --ui-navigation-menu-menu-trigger;
}
/* Chevron points down by default, flips up while its popover is open (no JS) */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
transition: var(--ui-navigation-menu-icon-transition);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="item"]:has([data-navigation-menu-slot~="popover"]:popover-open)
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
transform: var(--ui-navigation-menu-icon-transform);
}
/* ===========================================================================
POPOVER
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] {
position-anchor: --ui-navigation-menu-menu-trigger;
max-inline-size: var(--ui-navigation-menu-popover-w);
--ui-popover-p: 0;
--ui-popover-min-w: 0;
--ui-popover-position-area: block-end span-inline-end;
--ui-popover-origin: top center;
--ui-popover-my: calc(var(--spacing) * 1.5);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] [data-ui~="button"] {
--ui-button-rounded: var(--radius-xs);
}
/* ===========================================================================
ALIGNMENT — panels always open downward and set placement directly;
popover.css's [data-popover-side] matrix never applies to them.
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="center"] {
--ui-popover-position-area: block-end span-all;
--ui-popover-origin: top center;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="end"] {
--ui-popover-position-area: block-end span-inline-start;
--ui-popover-origin: top right;
}
/* ===========================================================================
SUBMENU — nested native popovers: the sub-trigger lives inside the parent
panel, so opening a submenu keeps the panel open.
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu"] {
anchor-scope: --ui-navigation-menu-menu-subtrigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] {
anchor-name: --ui-navigation-menu-menu-subtrigger;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
inline-size: 100%;
padding: var(--ui-navigation-menu-popover-gap);
border: 0;
border-radius: var(--ui-navigation-menu-popover-child-rounded);
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="submenu"]:has(
[data-navigation-menu-slot~="popover"][data-navigation-menu-variant="submenu"]:popover-open
)
[data-navigation-menu-slot~="submenu-trigger"] {
background-color: var(--color-muted);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"]:hover {
background-color: var(--color-muted);
}
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] > svg {
inline-size: var(--space-sm);
block-size: var(--space-sm);
flex: 0 0 auto;
color: var(--color-muted-foreground);
}
/* Inherits the base popover styling; only anchor and direction differ:
opens at the inline-end edge, flipping when near the viewport edge. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-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-my: 0;
--ui-popover-mx: calc(var(--spacing) * 1.5);
--ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}
/* ===========================================================================
PANEL CONTENT
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="viewport"] {
display: var(--ui-navigation-menu-popover-viewport-display);
grid-template-columns: var(--ui-navigation-menu-popover-viewport-template-cols);
align-items: var(--ui-navigation-menu-popover-viewport-items);
justify-items: var(--ui-navigation-menu-popover-viewport-justify-items);
gap: var(--ui-navigation-menu-popover-viewport-gap);
min-inline-size: var(--ui-navigation-menu-popover-viewport-min-w);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"] {
display: flex;
flex-direction: column;
padding: var(--ui-navigation-menu-popover-gap);
border-radius: var(--ui-navigation-menu-popover-child-rounded);
text-decoration: none;
transition: background-color var(--default-transition-duration)
var(--default-transition-timing-function);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"]:hover {
background-color: var(--color-muted);
}
}
/* ===========================================================================
MEGAMENU ANIMATION
=========================================================================== */
/* Block-size reveal on the viewport, not scaleY. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"][data-navigation-menu-animation="slide-down"] {
--ui-popover-scale--start: 1;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]
> [data-navigation-menu-slot~="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);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]: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));
}
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]:popover-open
[data-navigation-menu-slot~="viewport"] {
block-size: auto;
transform: scale(1) translateY(0);
@starting-style {
block-size: 0;
transform: translateY(0.5rem);
}
}
/* ===========================================================================
SIZING VARIANTS
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="container"] {
position-anchor: --ui-navigation-menu-menu-list;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="root"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: 100vi;
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
}Featured
<!-- Content-site panel layout: link rows beside a featured card. -->
<header data-ui="layout" style="--w: 100%">
<nav data-ui="navigation-menu">
<menu data-navigation-menu-slot="list">
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-featured-1"
>
Magazine
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
data-popover-align="start"
popover="auto"
data-navigation-menu-animation="slide-down"
>
<div
data-navigation-menu-slot="viewport"
style="
--display: grid;
--grid-cols: 3;
"
>
<div
style="
--display: flex-col;
--col-span: 2;
--py: 2;
--pl: 2;
--gap: 1px;
"
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Latest issue</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>This month's stories.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Interviews</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Conversations with makers.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Essays</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Long reads on craft.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Archive</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Every issue since day one.</span
>
</a>
</div>
<div
style="
--display: grid;
--py: 2;
--pr: 2;
"
>
<a
href="#"
style="
--display: flex-col;
--items: start;
--justify: end;
--p: 4;
--bg: var(--color-muted);
--opacity: 1;
--opacity--hover: 0.75;
--transition: var(--default-transition);
--rounded: var(--radius-sm);
"
>
<span
style="
--display: inline-flex;
--mb: 2;
--font-size: var(--font-size-eyebrow);
--font-weight: var(--font-weight-eyebrow);
--leading: var(--leading-eyebrow);
--tracking: var(--tracking-eyebrow);
--text-transform: uppercase;
--text: var(--color-muted-foreground);
"
>Featured</span
>
<span
style="
--font-size: var(--font-size-md);
--font-weight: strong;
--leading: var(--leading-md);
--tracking: var(--tracking-md);
"
>The typography issue</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text-wrap: pretty;
--text: var(--color-muted-foreground);
"
>
A whole issue on letters, grids, and rhythm.
</span>
</a>
</div>
</div>
</div>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">Newsletter</a>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">About</a>
</li>
</menu>
</nav>
</header>/**
* navigation-menu.css: Navigation menu ([data-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): rotates the trigger's data-navigation-menu-slot="marker"
* chevron without JS
* @uses @starting-style: slide-down viewport reveal
* @tokens --ui-navigation-menu-* (@layer variables)
* @presets on the popover slot: data-navigation-menu-variant="submenu",
* data-navigation-menu-size="container | root | screen",
* data-navigation-menu-animation="slide-down"
*/
@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-rounded: var(--radius-sm);
--ui-navigation-menu-popover-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-w: min(var(--article-sm), 100% - var(--space-md));
--ui-navigation-menu-popover-viewport-display: grid;
--ui-navigation-menu-popover-viewport-items: stretch;
--ui-navigation-menu-popover-viewport-justify-items: stretch;
--ui-navigation-menu-popover-viewport-template-cols: 1fr;
--ui-navigation-menu-popover-viewport-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-viewport-min-w: calc(var(--spacing) * 52);
}
}
@layer ui.components {
:where([data-ui~="navigation-menu"]) {
--ui-popover-shadow: var(--ui-navigation-menu-popover-shadow);
display: inline-flex;
position: relative;
anchor-name: --ui-navigation-menu-menu-root;
isolation: isolate;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="list"] {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ui-navigation-menu-popover-gap);
anchor-name: --ui-navigation-menu-menu-list;
}
/* Per-item anchor scope lets every trigger share one name */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="item"] {
anchor-scope: --ui-navigation-menu-menu-trigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="trigger"] {
anchor-name: --ui-navigation-menu-menu-trigger;
}
/* Chevron points down by default, flips up while its popover is open (no JS) */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
transition: var(--ui-navigation-menu-icon-transition);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="item"]:has([data-navigation-menu-slot~="popover"]:popover-open)
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
transform: var(--ui-navigation-menu-icon-transform);
}
/* ===========================================================================
POPOVER
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] {
position-anchor: --ui-navigation-menu-menu-trigger;
max-inline-size: var(--ui-navigation-menu-popover-w);
--ui-popover-p: 0;
--ui-popover-min-w: 0;
--ui-popover-position-area: block-end span-inline-end;
--ui-popover-origin: top center;
--ui-popover-my: calc(var(--spacing) * 1.5);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] [data-ui~="button"] {
--ui-button-rounded: var(--radius-xs);
}
/* ===========================================================================
ALIGNMENT — panels always open downward and set placement directly;
popover.css's [data-popover-side] matrix never applies to them.
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="center"] {
--ui-popover-position-area: block-end span-all;
--ui-popover-origin: top center;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="end"] {
--ui-popover-position-area: block-end span-inline-start;
--ui-popover-origin: top right;
}
/* ===========================================================================
SUBMENU — nested native popovers: the sub-trigger lives inside the parent
panel, so opening a submenu keeps the panel open.
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu"] {
anchor-scope: --ui-navigation-menu-menu-subtrigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] {
anchor-name: --ui-navigation-menu-menu-subtrigger;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
inline-size: 100%;
padding: var(--ui-navigation-menu-popover-gap);
border: 0;
border-radius: var(--ui-navigation-menu-popover-child-rounded);
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="submenu"]:has(
[data-navigation-menu-slot~="popover"][data-navigation-menu-variant="submenu"]:popover-open
)
[data-navigation-menu-slot~="submenu-trigger"] {
background-color: var(--color-muted);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"]:hover {
background-color: var(--color-muted);
}
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] > svg {
inline-size: var(--space-sm);
block-size: var(--space-sm);
flex: 0 0 auto;
color: var(--color-muted-foreground);
}
/* Inherits the base popover styling; only anchor and direction differ:
opens at the inline-end edge, flipping when near the viewport edge. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-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-my: 0;
--ui-popover-mx: calc(var(--spacing) * 1.5);
--ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}
/* ===========================================================================
PANEL CONTENT
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="viewport"] {
display: var(--ui-navigation-menu-popover-viewport-display);
grid-template-columns: var(--ui-navigation-menu-popover-viewport-template-cols);
align-items: var(--ui-navigation-menu-popover-viewport-items);
justify-items: var(--ui-navigation-menu-popover-viewport-justify-items);
gap: var(--ui-navigation-menu-popover-viewport-gap);
min-inline-size: var(--ui-navigation-menu-popover-viewport-min-w);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"] {
display: flex;
flex-direction: column;
padding: var(--ui-navigation-menu-popover-gap);
border-radius: var(--ui-navigation-menu-popover-child-rounded);
text-decoration: none;
transition: background-color var(--default-transition-duration)
var(--default-transition-timing-function);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"]:hover {
background-color: var(--color-muted);
}
}
/* ===========================================================================
MEGAMENU ANIMATION
=========================================================================== */
/* Block-size reveal on the viewport, not scaleY. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"][data-navigation-menu-animation="slide-down"] {
--ui-popover-scale--start: 1;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]
> [data-navigation-menu-slot~="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);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]: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));
}
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]:popover-open
[data-navigation-menu-slot~="viewport"] {
block-size: auto;
transform: scale(1) translateY(0);
@starting-style {
block-size: 0;
transform: translateY(0.5rem);
}
}
/* ===========================================================================
SIZING VARIANTS
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="container"] {
position-anchor: --ui-navigation-menu-menu-list;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="root"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: 100vi;
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
}Icon grid
<!-- Content-site panel layout: a grid of icon tiles. -->
<header data-ui="layout" style="--w: 100%">
<nav data-ui="navigation-menu">
<menu data-navigation-menu-slot="list">
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-icon-grid-1"
>
Solutions
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
data-popover-align="start"
popover="auto"
data-navigation-menu-animation="slide-down"
style="
--w: var(--ui-navigation-menu-popover-w);
--ui-navigation-menu-popover-w: min(var(--layout-sm), 100% - var(--space-md));
"
>
<div
data-navigation-menu-slot="viewport"
style="
--display: grid;
--grid-cols: 3;
--p: 2;
"
>
<a
href="#"
data-navigation-menu-slot="link"
style="
--display: flex;
--items: start;
--gap: 4;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
aria-hidden="true"
style="
--shrink: 0;
--size: 6;
--text: var(--color-muted-foreground);
"
>
<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 style="--display: flex-col">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Analytics</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Understand your traffic.</span
>
</span>
</a>
<a
href="#"
data-navigation-menu-slot="link"
style="
--display: flex;
--items: start;
--gap: 4;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
aria-hidden="true"
style="
--shrink: 0;
--size: 6;
--text: var(--color-muted-foreground);
"
>
<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 style="--display: flex-col">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Uptime</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Monitoring and alerts.</span
>
</span>
</a>
<a
href="#"
data-navigation-menu-slot="link"
style="
--display: flex;
--items: start;
--gap: 4;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
aria-hidden="true"
style="
--shrink: 0;
--size: 6;
--text: var(--color-muted-foreground);
"
>
<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 style="--display: flex-col">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Dashboards</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Your metrics, one page.</span
>
</span>
</a>
<a
href="#"
data-navigation-menu-slot="link"
style="
--display: flex;
--items: start;
--gap: 4;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
aria-hidden="true"
style="
--shrink: 0;
--size: 6;
--text: var(--color-muted-foreground);
"
>
<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 style="--display: flex-col">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Exports</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Take your data anywhere.</span
>
</span>
</a>
<a
href="#"
data-navigation-menu-slot="link"
style="
--display: flex;
--items: start;
--gap: 4;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
aria-hidden="true"
style="
--shrink: 0;
--size: 6;
--text: var(--color-muted-foreground);
"
>
<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 style="--display: flex-col">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Teams</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Roles and permissions.</span
>
</span>
</a>
<a
href="#"
data-navigation-menu-slot="link"
style="
--display: flex;
--items: start;
--gap: 4;
"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
aria-hidden="true"
style="
--shrink: 0;
--size: 6;
--text: var(--color-muted-foreground);
"
>
<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 style="--display: flex-col">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>API</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Automate everything.</span
>
</span>
</a>
</div>
</div>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">Docs</a>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">Pricing</a>
</li>
</menu>
</nav>
</header>/**
* navigation-menu.css: Navigation menu ([data-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): rotates the trigger's data-navigation-menu-slot="marker"
* chevron without JS
* @uses @starting-style: slide-down viewport reveal
* @tokens --ui-navigation-menu-* (@layer variables)
* @presets on the popover slot: data-navigation-menu-variant="submenu",
* data-navigation-menu-size="container | root | screen",
* data-navigation-menu-animation="slide-down"
*/
@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-rounded: var(--radius-sm);
--ui-navigation-menu-popover-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-w: min(var(--article-sm), 100% - var(--space-md));
--ui-navigation-menu-popover-viewport-display: grid;
--ui-navigation-menu-popover-viewport-items: stretch;
--ui-navigation-menu-popover-viewport-justify-items: stretch;
--ui-navigation-menu-popover-viewport-template-cols: 1fr;
--ui-navigation-menu-popover-viewport-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-viewport-min-w: calc(var(--spacing) * 52);
}
}
@layer ui.components {
:where([data-ui~="navigation-menu"]) {
--ui-popover-shadow: var(--ui-navigation-menu-popover-shadow);
display: inline-flex;
position: relative;
anchor-name: --ui-navigation-menu-menu-root;
isolation: isolate;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="list"] {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ui-navigation-menu-popover-gap);
anchor-name: --ui-navigation-menu-menu-list;
}
/* Per-item anchor scope lets every trigger share one name */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="item"] {
anchor-scope: --ui-navigation-menu-menu-trigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="trigger"] {
anchor-name: --ui-navigation-menu-menu-trigger;
}
/* Chevron points down by default, flips up while its popover is open (no JS) */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
transition: var(--ui-navigation-menu-icon-transition);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="item"]:has([data-navigation-menu-slot~="popover"]:popover-open)
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
transform: var(--ui-navigation-menu-icon-transform);
}
/* ===========================================================================
POPOVER
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] {
position-anchor: --ui-navigation-menu-menu-trigger;
max-inline-size: var(--ui-navigation-menu-popover-w);
--ui-popover-p: 0;
--ui-popover-min-w: 0;
--ui-popover-position-area: block-end span-inline-end;
--ui-popover-origin: top center;
--ui-popover-my: calc(var(--spacing) * 1.5);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] [data-ui~="button"] {
--ui-button-rounded: var(--radius-xs);
}
/* ===========================================================================
ALIGNMENT — panels always open downward and set placement directly;
popover.css's [data-popover-side] matrix never applies to them.
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="center"] {
--ui-popover-position-area: block-end span-all;
--ui-popover-origin: top center;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="end"] {
--ui-popover-position-area: block-end span-inline-start;
--ui-popover-origin: top right;
}
/* ===========================================================================
SUBMENU — nested native popovers: the sub-trigger lives inside the parent
panel, so opening a submenu keeps the panel open.
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu"] {
anchor-scope: --ui-navigation-menu-menu-subtrigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] {
anchor-name: --ui-navigation-menu-menu-subtrigger;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
inline-size: 100%;
padding: var(--ui-navigation-menu-popover-gap);
border: 0;
border-radius: var(--ui-navigation-menu-popover-child-rounded);
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="submenu"]:has(
[data-navigation-menu-slot~="popover"][data-navigation-menu-variant="submenu"]:popover-open
)
[data-navigation-menu-slot~="submenu-trigger"] {
background-color: var(--color-muted);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"]:hover {
background-color: var(--color-muted);
}
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] > svg {
inline-size: var(--space-sm);
block-size: var(--space-sm);
flex: 0 0 auto;
color: var(--color-muted-foreground);
}
/* Inherits the base popover styling; only anchor and direction differ:
opens at the inline-end edge, flipping when near the viewport edge. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-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-my: 0;
--ui-popover-mx: calc(var(--spacing) * 1.5);
--ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}
/* ===========================================================================
PANEL CONTENT
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="viewport"] {
display: var(--ui-navigation-menu-popover-viewport-display);
grid-template-columns: var(--ui-navigation-menu-popover-viewport-template-cols);
align-items: var(--ui-navigation-menu-popover-viewport-items);
justify-items: var(--ui-navigation-menu-popover-viewport-justify-items);
gap: var(--ui-navigation-menu-popover-viewport-gap);
min-inline-size: var(--ui-navigation-menu-popover-viewport-min-w);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"] {
display: flex;
flex-direction: column;
padding: var(--ui-navigation-menu-popover-gap);
border-radius: var(--ui-navigation-menu-popover-child-rounded);
text-decoration: none;
transition: background-color var(--default-transition-duration)
var(--default-transition-timing-function);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"]:hover {
background-color: var(--color-muted);
}
}
/* ===========================================================================
MEGAMENU ANIMATION
=========================================================================== */
/* Block-size reveal on the viewport, not scaleY. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"][data-navigation-menu-animation="slide-down"] {
--ui-popover-scale--start: 1;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]
> [data-navigation-menu-slot~="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);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]: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));
}
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]:popover-open
[data-navigation-menu-slot~="viewport"] {
block-size: auto;
transform: scale(1) translateY(0);
@starting-style {
block-size: 0;
transform: translateY(0.5rem);
}
}
/* ===========================================================================
SIZING VARIANTS
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="container"] {
position-anchor: --ui-navigation-menu-menu-list;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="root"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: 100vi;
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
}Megamenu
<!-- Full-width megamenu: data-navigation-menu-size="container" spans the menu bar; the panel
is a 4-column grid of eyebrow-labeled link groups. -->
<header data-ui="layout" style="--w: 100%">
<nav data-ui="navigation-menu">
<menu data-navigation-menu-slot="list">
<li data-navigation-menu-slot="item">
<button
data-ui="button"
data-navigation-menu-slot="trigger"
data-button-variant="ghost"
type="button"
popovertarget="nav-megamenu-1"
>
Shop
<svg
data-navigation-menu-slot="marker"
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-navigation-menu-slot="popover"
popover="auto"
data-navigation-menu-size="container"
data-navigation-menu-animation="slide-down"
>
<div
data-navigation-menu-slot="viewport"
style="
--display: grid;
--grid-cols: 4;
--p: 2;
--gap: 4;
"
>
<div
style="
--display: grid;
--row-span: 5;
--template-rows: subgrid;
--gap: 1px;
"
>
<span
style="
--display: inline-flex;
--pt: 2;
--pb: 2;
--font-size: var(--font-size-eyebrow);
--font-weight: var(--font-weight-eyebrow);
--leading: var(--leading-eyebrow);
--tracking: var(--tracking-eyebrow);
--text-transform: uppercase;
--text: var(--color-muted-foreground);
"
>Dresses</span
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Mini & Midi</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Everyday lengths for work and weekends.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Maxi Dresses</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Flowing silhouettes in linen and cotton.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Jumpsuits</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>One-piece outfits, easy to style.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Shop all dresses</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Browse the full collection.</span
>
</a>
</div>
<div
style="
--display: grid;
--row-span: 5;
--template-rows: subgrid;
--gap: 1px;
"
>
<span
style="
--display: inline-flex;
--pt: 2;
--pb: 2;
--font-size: var(--font-size-eyebrow);
--font-weight: var(--font-weight-eyebrow);
--leading: var(--leading-eyebrow);
--tracking: var(--tracking-eyebrow);
--text-transform: uppercase;
--text: var(--color-muted-foreground);
"
>Tops</span
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>T-shirts & Tanks</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Basics in organic cotton blends.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Blouses & Shirts</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Office-ready and off-duty styles.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Sweaters & Cardigans</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Layering pieces for cooler days.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Activewear</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Leggings, sports bras, and sets.</span
>
</a>
</div>
<div
style="
--display: grid;
--row-span: 5;
--template-rows: subgrid;
--gap: 1px;
"
>
<span
style="
--display: inline-flex;
--pt: 2;
--pb: 2;
--font-size: var(--font-size-eyebrow);
--font-weight: var(--font-weight-eyebrow);
--leading: var(--leading-eyebrow);
--tracking: var(--tracking-eyebrow);
--text-transform: uppercase;
--text: var(--color-muted-foreground);
"
>Shirts</span
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>T-shirts & Polos</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Classic fits in breathable fabrics.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Oxford & Dress Shirts</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Collared styles for work and events.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Henleys & Sweaters</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Casual layers for transitional weather.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Shop all shirts</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>See every cut and color.</span
>
</a>
</div>
<div
style="
--display: grid;
--row-span: 5;
--template-rows: subgrid;
--gap: 1px;
"
>
<span
style="
--display: inline-flex;
--pt: 2;
--pb: 2;
--font-size: var(--font-size-eyebrow);
--font-weight: var(--font-weight-eyebrow);
--leading: var(--leading-eyebrow);
--tracking: var(--tracking-eyebrow);
--text-transform: uppercase;
--text: var(--color-muted-foreground);
"
>Bedding</span
>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Sheet Sets</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Percale, sateen, and linen weaves.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Duvets & Comforters</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>All-season weights and down alternatives.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Pillows & Throws</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Accent layers for beds and sofas.</span
>
</a>
<a href="#" data-navigation-menu-slot="link">
<span
style="
--font-size: var(--font-size-sm);
--font-weight: strong;
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
"
>Shop all bedding</span
>
<span
style="
--font-size: var(--font-size-sm);
--leading: var(--leading-sm);
--tracking: var(--tracking-sm);
--text: var(--color-muted-foreground);
"
>Build a complete sleep setup.</span
>
</a>
</div>
</div>
</div>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">New arrivals</a>
</li>
<li data-navigation-menu-slot="item">
<a href="#" data-ui="button" data-button-variant="ghost">Sale</a>
</li>
</menu>
</nav>
</header>/**
* navigation-menu.css: Navigation menu ([data-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): rotates the trigger's data-navigation-menu-slot="marker"
* chevron without JS
* @uses @starting-style: slide-down viewport reveal
* @tokens --ui-navigation-menu-* (@layer variables)
* @presets on the popover slot: data-navigation-menu-variant="submenu",
* data-navigation-menu-size="container | root | screen",
* data-navigation-menu-animation="slide-down"
*/
@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-rounded: var(--radius-sm);
--ui-navigation-menu-popover-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-w: min(var(--article-sm), 100% - var(--space-md));
--ui-navigation-menu-popover-viewport-display: grid;
--ui-navigation-menu-popover-viewport-items: stretch;
--ui-navigation-menu-popover-viewport-justify-items: stretch;
--ui-navigation-menu-popover-viewport-template-cols: 1fr;
--ui-navigation-menu-popover-viewport-gap: calc(var(--spacing) * 1.5);
--ui-navigation-menu-popover-viewport-min-w: calc(var(--spacing) * 52);
}
}
@layer ui.components {
:where([data-ui~="navigation-menu"]) {
--ui-popover-shadow: var(--ui-navigation-menu-popover-shadow);
display: inline-flex;
position: relative;
anchor-name: --ui-navigation-menu-menu-root;
isolation: isolate;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="list"] {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ui-navigation-menu-popover-gap);
anchor-name: --ui-navigation-menu-menu-list;
}
/* Per-item anchor scope lets every trigger share one name */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="item"] {
anchor-scope: --ui-navigation-menu-menu-trigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="trigger"] {
anchor-name: --ui-navigation-menu-menu-trigger;
}
/* Chevron points down by default, flips up while its popover is open (no JS) */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
inline-size: var(--space-sm);
block-size: var(--space-sm);
transition: var(--ui-navigation-menu-icon-transition);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="item"]:has([data-navigation-menu-slot~="popover"]:popover-open)
[data-navigation-menu-slot~="trigger"]
> [data-navigation-menu-slot~="marker"] {
transform: var(--ui-navigation-menu-icon-transform);
}
/* ===========================================================================
POPOVER
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] {
position-anchor: --ui-navigation-menu-menu-trigger;
max-inline-size: var(--ui-navigation-menu-popover-w);
--ui-popover-p: 0;
--ui-popover-min-w: 0;
--ui-popover-position-area: block-end span-inline-end;
--ui-popover-origin: top center;
--ui-popover-my: calc(var(--spacing) * 1.5);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="popover"] [data-ui~="button"] {
--ui-button-rounded: var(--radius-xs);
}
/* ===========================================================================
ALIGNMENT — panels always open downward and set placement directly;
popover.css's [data-popover-side] matrix never applies to them.
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="center"] {
--ui-popover-position-area: block-end span-all;
--ui-popover-origin: top center;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-popover-align="end"] {
--ui-popover-position-area: block-end span-inline-start;
--ui-popover-origin: top right;
}
/* ===========================================================================
SUBMENU — nested native popovers: the sub-trigger lives inside the parent
panel, so opening a submenu keeps the panel open.
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu"] {
anchor-scope: --ui-navigation-menu-menu-subtrigger;
position: relative;
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] {
anchor-name: --ui-navigation-menu-menu-subtrigger;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
inline-size: 100%;
padding: var(--ui-navigation-menu-popover-gap);
border: 0;
border-radius: var(--ui-navigation-menu-popover-child-rounded);
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="submenu"]:has(
[data-navigation-menu-slot~="popover"][data-navigation-menu-variant="submenu"]:popover-open
)
[data-navigation-menu-slot~="submenu-trigger"] {
background-color: var(--color-muted);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"]:hover {
background-color: var(--color-muted);
}
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="submenu-trigger"] > svg {
inline-size: var(--space-sm);
block-size: var(--space-sm);
flex: 0 0 auto;
color: var(--color-muted-foreground);
}
/* Inherits the base popover styling; only anchor and direction differ:
opens at the inline-end edge, flipping when near the viewport edge. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-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-my: 0;
--ui-popover-mx: calc(var(--spacing) * 1.5);
--ui-popover-position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;
}
/* ===========================================================================
PANEL CONTENT
=========================================================================== */
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="viewport"] {
display: var(--ui-navigation-menu-popover-viewport-display);
grid-template-columns: var(--ui-navigation-menu-popover-viewport-template-cols);
align-items: var(--ui-navigation-menu-popover-viewport-items);
justify-items: var(--ui-navigation-menu-popover-viewport-justify-items);
gap: var(--ui-navigation-menu-popover-viewport-gap);
min-inline-size: var(--ui-navigation-menu-popover-viewport-min-w);
}
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"] {
display: flex;
flex-direction: column;
padding: var(--ui-navigation-menu-popover-gap);
border-radius: var(--ui-navigation-menu-popover-child-rounded);
text-decoration: none;
transition: background-color var(--default-transition-duration)
var(--default-transition-timing-function);
}
@media (hover: hover) {
:where([data-ui~="navigation-menu"]) [data-navigation-menu-slot~="link"]:hover {
background-color: var(--color-muted);
}
}
/* ===========================================================================
MEGAMENU ANIMATION
=========================================================================== */
/* Block-size reveal on the viewport, not scaleY. */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"][data-navigation-menu-animation="slide-down"] {
--ui-popover-scale--start: 1;
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]
> [data-navigation-menu-slot~="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);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]: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));
}
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-animation="slide-down"]:popover-open
[data-navigation-menu-slot~="viewport"] {
block-size: auto;
transform: scale(1) translateY(0);
@starting-style {
block-size: 0;
transform: translateY(0.5rem);
}
}
/* ===========================================================================
SIZING VARIANTS
=========================================================================== */
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="container"] {
position-anchor: --ui-navigation-menu-menu-list;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="root"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: anchor-size();
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
:where([data-ui~="navigation-menu"])
[data-navigation-menu-slot~="popover"][data-navigation-menu-size="screen"] {
position-anchor: --ui-navigation-menu-menu-root;
--ui-navigation-menu-popover-w: 100vi;
--ui-popover-position-area: block-end span-all;
inline-size: var(--ui-navigation-menu-popover-w);
}
}Attributes
| Attribute | Kind | Values | Notes |
|---|---|---|---|
data-ui="navigation-menu" | Identity | free value | Identity token in data-ui. |
data-navigation-menu-animation | Preset | slide-down | Preset: a named bundle of values. |
data-navigation-menu-size | Preset | container root screen | Preset: a named bundle of values. |
data-navigation-menu-variant | Preset | submenu | Preset: a named bundle of values. |
data-navigation-menu-slot | Slot | item link list marker popover submenu submenu-trigger trigger viewport | Slot: a part inside the primitive. |
Hooks
Set a hook on :root, on a subtree, or inline on one element. A state hook (--…--hover) themes that state only; the primitive keeps its other states.
| Hook | Default | State |
|---|---|---|
--ui-navigation-menu-icon-transform | rotate(-180deg) | — |
--ui-navigation-menu-icon-transition | transform var(--default-transition-duration) var(--default-transition-timing-function) | — |
--ui-navigation-menu-popover-child-rounded | var(--radius-sm) | — |
--ui-navigation-menu-popover-gap | calc(var(--spacing) * 1.5) | — |
--ui-navigation-menu-popover-shadow | var(--shadow-md) | — |
--ui-navigation-menu-popover-viewport-display | grid | — |
--ui-navigation-menu-popover-viewport-gap | calc(var(--spacing) * 1.5) | — |
--ui-navigation-menu-popover-viewport-items | stretch | — |
--ui-navigation-menu-popover-viewport-justify-items | stretch | — |
--ui-navigation-menu-popover-viewport-min-w | calc(var(--spacing) * 52) | — |
--ui-navigation-menu-popover-viewport-template-cols | 1fr | — |
--ui-navigation-menu-popover-w | min(var(--article-sm), 100% - var(--space-md)) | — |