Default

<nav class="nav-menu" aria-label="Main">
  <ul class="nav-menu-list">
    <li class="nav-menu-item"><a class="nav-menu-link" href="#">Home</a></li>
    <li class="nav-menu-item">
      <button class="nav-menu-trigger" popovertarget="dd">Products</button>
      <div class="nav-menu-content" id="dd" popover>...</div>
    </li>
  </ul>
</nav>

With dropdowns

Triggers open anchor-positioned content panels. Chevron rotates when open.

<nav class="nav-menu" aria-label="Site navigation">
  <ul class="nav-menu-list">
    <li class="nav-menu-item">
      <button class="nav-menu-trigger" popovertarget="nav-products">
        Products
        <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24"
             fill="none" stroke="currentColor" stroke-width="2">
          <path d="m6 9 6 6 6-6"/>
        </svg>
      </button>
      <div class="nav-menu-content" id="nav-products" popover>
        <a class="nav-menu-content-link" href="#">
          <strong>Analytics</strong>
          <p>View your dashboard and metrics</p>
        </a>
        <a class="nav-menu-content-link" href="#">...</a>
      </div>
    </li>
    <li class="nav-menu-item">
      <a class="nav-menu-link" href="#">Pricing</a>
    </li>
  </ul>
</nav>

CSS view file

Styles for the navigation-menu component. Uses design tokens for colors, spacing, and radius.

@layer components {
  .nav-menu-list { display: flex; align-items: center; gap: 0.25rem; list-style: none; margin: 0; padding: 0; }
  .nav-menu-link, .nav-menu-trigger {
    display: inline-flex; align-items: center; gap: 0.25rem;
    padding: 0.5rem 0.75rem; border: none; border-radius: var(--radius-md);
    background: transparent; color: var(--foreground); font-size: 0.875rem; font-weight: 500;
    font-family: inherit; text-decoration: none; cursor: pointer;
    transition: background-color 150ms ease, color 150ms ease;
    outline: none;
    &:hover { background-color: var(--accent); color: var(--accent-foreground); }
    &:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
    & svg { width: 0.875rem; height: 0.875rem; color: var(--muted-foreground); transition: transform 200ms ease; }
  }

  /* Rotate chevron when popover is open */
  .nav-menu-trigger:has(+ .nav-menu-content:popover-open) svg {
    transform: rotate(180deg);
  }

  .nav-menu-content {
    position: fixed; inset: auto;
    margin: 0; border: 1px solid var(--border); border-radius: var(--radius-xl);
    background-color: var(--popover); color: var(--popover-foreground);
    padding: 1rem; box-shadow: var(--shadow-md); min-width: 14rem;
    opacity: 0; transform: translateY(-4px);
    transition: opacity 150ms ease, transform 150ms ease, display 150ms allow-discrete;

    /* -- Anchor positioning -- */
    top: anchor(bottom); left: anchor(left); margin-top: 4px;
    position-try-fallbacks: flip-block;

    &:popover-open { opacity: 1; transform: translateY(0); }
  }
  @starting-style { .nav-menu-content:popover-open { opacity: 0; transform: translateY(-4px); } }
  .nav-menu-content-link {
    display: block; padding: 0.5rem 0.75rem; border-radius: var(--radius-md);
    text-decoration: none; color: var(--foreground); font-size: 0.875rem;
    transition: background-color 150ms ease;
    &:hover { background-color: var(--accent); }
    &:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
    & p { margin: 0.125rem 0 0; font-size: 0.75rem; color: var(--muted-foreground); font-weight: 400; }
  }

  /* -- Accessibility ------------------------------------------ */

  @media (prefers-reduced-motion: reduce) {
    .nav-menu-link, .nav-menu-trigger,
    .nav-menu-content, .nav-menu-content-link {
      transition: none;
    }
    .nav-menu-trigger svg { transition: none; }
  }

  @media (forced-colors: active) {
    .nav-menu-content {
      border-color: ButtonText;
    }
  }
}

JavaScript view file

Sets CSS anchor positioning names for trigger→content pairs. The popover API handles open/close natively.

// -- Navigation Menu -----------------------------------------
// CSS anchor positioning for dropdown navigation menus.

function init() {
  document.querySelectorAll('.nav-menu:not([data-init])').forEach((nav) => {
  nav.dataset.init = '';
  nav.querySelectorAll('.nav-menu-trigger[popovertarget]').forEach((trigger) => {
    const id = trigger.getAttribute('popovertarget');
    const content = document.getElementById(id);
    if (!content) return;

    // CSS anchor positioning - unique name per trigger-content pair
    const anchorId = `--nav-menu-${id}`;
    trigger.style.anchorName = anchorId;
    content.style.positionAnchor = anchorId;
  });
});
}

init();
new MutationObserver(init).observe(document, { childList: true, subtree: true });