Open demo

CSS code

HTML
<nav class="nvd" aria-label="Main">
  <a href="#" class="nvd-logo">Northwind</a>
  <ul class="nvd-links">
    <li class="nvd-item">
      <button class="nvd-top" type="button" aria-expanded="false" aria-controls="nvd-products">Products <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      <div class="nvd-drop" id="nvd-products">
        <p class="nvd-h" id="nvd-h-platform">Platform</p>
        <ul aria-labelledby="nvd-h-platform">
          <li><a href="#">Dashboard</a></li>
          <li><a href="#">Automations</a></li>
          <li><a href="#">Integrations</a></li>
        </ul>
        <p class="nvd-h" id="nvd-h-addons">Add-ons</p>
        <ul aria-labelledby="nvd-h-addons">
          <li><a href="#">Analytics</a></li>
          <li><a href="#">Audit log</a></li>
        </ul>
      </div>
    </li>
    <li class="nvd-item">
      <button class="nvd-top" type="button" aria-expanded="false" aria-controls="nvd-solutions">Solutions <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      <div class="nvd-drop" id="nvd-solutions">
        <ul>
          <li><a href="#">For startups</a></li>
          <li><a href="#">For agencies</a></li>
          <li><a href="#">For enterprise</a></li>
        </ul>
      </div>
    </li>
    <li><a class="nvd-top" href="#" aria-current="page">Pricing</a></li>
    <li><a class="nvd-top" href="#">Docs</a></li>
  </ul>
  <a href="#" class="nvd-cta">Start free</a>
</nav>
CSS
/* Navbar with dropdowns. Each top-level item is a disclosure button (aria-expanded + aria-controls):
   click, tap, Enter or Space opens it, Esc closes it. With a mouse the submenu also opens on hover. */
.nvd{position:relative;width:100%;background:#fff;border-bottom:1px solid #e7e9f0;display:flex;align-items:center;gap:6px;padding:0 30px;height:64px}
:where(.nvd) a{text-decoration:none;color:inherit}
:where(.nvd) ul{list-style:none;margin:0;padding:0}
:where(.nvd) svg{display:block}
.nvd-logo{font-weight:800;font-size:1.05rem;letter-spacing:-.02em;margin-right:22px}
.nvd-links{display:flex;align-items:center;gap:6px}
.nvd-item{position:relative}
.nvd-top{display:flex;align-items:center;gap:6px;padding:10px 14px;border:0;border-radius:9px;background:none;font:inherit;font-size:.9rem;font-weight:600;line-height:normal;color:#475467;cursor:pointer;transition:background-color .15s,color .15s}
.nvd-top:hover,.nvd-top[aria-expanded="true"],.nvd-top[aria-current="page"]{background:#f2f4f8;color:#101828}
.nvd-top svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2.4;transition:transform .2s}
.nvd-top[aria-expanded="true"] svg{transform:rotate(180deg)}
.nvd-drop{position:absolute;top:calc(100% + 8px);left:0;min-width:230px;background:#fff;border:1px solid #e7e9f0;border-radius:13px;padding:7px;opacity:0;visibility:hidden;transform:translateY(-6px);transition:opacity .17s,transform .17s,visibility .17s;box-shadow:0 20px 42px -18px rgba(16,24,40,.3);z-index:10}
.nvd-drop::before{content:"";position:absolute;left:0;right:0;bottom:100%;height:10px} /* bridges the gap so the pointer can reach the menu */
.nvd-top[aria-expanded="true"]+.nvd-drop{opacity:1;visibility:visible;transform:none}
@media (hover:hover){
  .nvd-item:hover:not([data-hover-off])>.nvd-drop{opacity:1;visibility:visible;transform:none}
  .nvd-item:hover:not([data-hover-off])>.nvd-top{background:#f2f4f8;color:#101828}
  .nvd-item:hover:not([data-hover-off])>.nvd-top svg{transform:rotate(180deg)}
}
.nvd-drop a{display:block;padding:9px 12px;border-radius:9px;font-size:.87rem;color:#475467}
.nvd-drop a:hover{background:#f2f4f8;color:#101828}
.nvd-h{margin:0;font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#667085;padding:9px 12px 5px}
.nvd-cta{margin-left:auto;background:#4f46e5;color:#fff;padding:10px 20px;border-radius:9px;font-size:.88rem;font-weight:700}
.nvd-cta:hover{background:#4338ca}
.nvd :focus-visible{outline:2px solid #4f46e5;outline-offset:2px}
.nvd-drop a:focus-visible{outline-offset:-2px}
@media (max-width:780px){
  .nvd{flex-wrap:wrap;height:auto;padding:10px 16px;row-gap:4px}
  .nvd-logo{margin-right:8px}
  .nvd-links{order:2;width:100%;flex-wrap:wrap;gap:2px}
  .nvd-item{position:static}
  .nvd-drop{left:16px;right:16px;min-width:0}
  .nvd-drop::before{height:20px}
}
@media (prefers-reduced-motion:reduce){.nvd-top,.nvd-top svg,.nvd-drop{transition:none}}
JavaScript
/* Disclosure navigation (WAI-ARIA pattern). Buttons toggle aria-expanded; opening one closes the others.
   Esc closes the open submenu (focus returns to its button) or dismisses a hover-opened one; a click outside
   or tabbing away also closes it. data-hover-off keeps a dismissed menu shut until the pointer leaves. */
document.querySelectorAll('.nvd').forEach(nav => {
  const btns = [...nav.querySelectorAll('button[aria-controls]')];
  const isOpen = b => b.getAttribute('aria-expanded') === 'true';
  const set = (b, open) => {
    b.setAttribute('aria-expanded', open);
    b.parentElement.toggleAttribute('data-hover-off', !open && b.parentElement.matches(':hover'));
  };
  btns.forEach(b => {
    const li = b.parentElement;
    b.addEventListener('click', () => { const open = !isOpen(b); btns.forEach(x => { if (x !== b && isOpen(x)) set(x, false); }); set(b, open); });
    li.addEventListener('focusout', e => { if (e.relatedTarget && !li.contains(e.relatedTarget) && isOpen(b)) set(b, false); });
    li.addEventListener('pointerleave', () => li.removeAttribute('data-hover-off'));
  });
  document.addEventListener('click', e => btns.forEach(b => { if (isOpen(b) && !b.parentElement.contains(e.target)) set(b, false); }));
  document.addEventListener('keydown', e => {
    if (e.key !== 'Escape') return;
    btns.forEach(b => {
      const li = b.parentElement;
      if (isOpen(b)) { const inside = li.contains(document.activeElement); set(b, false); if (inside) b.focus(); }
      else if (li.matches(':hover')) li.setAttribute('data-hover-off', '');
    });
  });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<nav class="nvd navbar navbar-expand" aria-label="Main">
  <a class="navbar-brand" href="#">Northwind</a>
  <ul class="navbar-nav">
    <li class="nav-item dropdown">
      <button class="nav-link" type="button" data-bs-toggle="dropdown" aria-expanded="false">Products <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      <div class="dropdown-menu">
        <p class="dropdown-header" id="nvd-h-platform">Platform</p>
        <ul class="list-unstyled m-0" aria-labelledby="nvd-h-platform">
          <li><a class="dropdown-item" href="#">Dashboard</a></li>
          <li><a class="dropdown-item" href="#">Automations</a></li>
          <li><a class="dropdown-item" href="#">Integrations</a></li>
        </ul>
        <p class="dropdown-header" id="nvd-h-addons">Add-ons</p>
        <ul class="list-unstyled m-0" aria-labelledby="nvd-h-addons">
          <li><a class="dropdown-item" href="#">Analytics</a></li>
          <li><a class="dropdown-item" href="#">Audit log</a></li>
        </ul>
      </div>
    </li>
    <li class="nav-item dropdown">
      <button class="nav-link" type="button" data-bs-toggle="dropdown" aria-expanded="false">Solutions <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      <ul class="dropdown-menu">
        <li><a class="dropdown-item" href="#">For startups</a></li>
        <li><a class="dropdown-item" href="#">For agencies</a></li>
        <li><a class="dropdown-item" href="#">For enterprise</a></li>
      </ul>
    </li>
    <li class="nav-item"><a class="nav-link active" href="#" aria-current="page">Pricing</a></li>
    <li class="nav-item"><a class="nav-link" href="#">Docs</a></li>
  </ul>
  <a class="btn nvd-cta" href="#">Start free</a>
</nav>
CSS
/* Bootstrap .navbar.navbar-expand with .dropdown menus. The Dropdown plugin sets aria-expanded, opens on click, tap,
   Enter, Space or ArrowDown, moves through the links with the arrow keys and closes on Esc (focus returns to the button).
   With a mouse the menus also open on hover; a few lines of script let Esc dismiss a hover-opened menu. */
.nvd{--bs-navbar-padding-x:30px;--bs-navbar-padding-y:0;--bs-navbar-brand-color:#101828;--bs-navbar-brand-hover-color:#101828;--bs-navbar-brand-font-size:1.05rem;
  --bs-navbar-brand-margin-end:22px;--bs-navbar-brand-padding-y:0;--bs-navbar-color:#475467;--bs-navbar-hover-color:#101828;--bs-navbar-active-color:#101828;--bs-navbar-nav-link-padding-x:14px;
  height:64px;background:#fff;border-bottom:1px solid #e7e9f0;gap:6px}
.nvd .navbar-brand{font-weight:800;letter-spacing:-.02em;border-radius:4px}
.nvd .navbar-nav{gap:6px}
.nvd .nav-link{--bs-nav-link-padding-y:10px;--bs-nav-link-font-size:.9rem;--bs-nav-link-font-weight:600;display:flex;align-items:center;gap:6px;border-radius:9px;line-height:normal}
.nvd .nav-link:hover,.nvd .nav-link.show,.nvd .nav-link.active{background:#f2f4f8}
.nvd .nav-link svg{display:block;width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2.4;transition:transform .2s}
.nvd .nav-link.show svg{transform:rotate(180deg)}
.nvd .dropdown-menu{--bs-dropdown-min-width:230px;--bs-dropdown-padding-x:7px;--bs-dropdown-padding-y:7px;--bs-dropdown-border-color:#e7e9f0;--bs-dropdown-border-radius:13px;
  --bs-dropdown-font-size:.87rem;--bs-dropdown-link-color:#475467;--bs-dropdown-link-hover-color:#101828;--bs-dropdown-link-hover-bg:#f2f4f8;--bs-dropdown-link-active-color:#101828;
  --bs-dropdown-link-active-bg:#f2f4f8;--bs-dropdown-item-padding-x:12px;--bs-dropdown-item-padding-y:9px;--bs-dropdown-item-border-radius:9px;
  --bs-dropdown-header-color:#667085;--bs-dropdown-header-padding-x:12px;--bs-dropdown-header-padding-y:9px;
  top:calc(100% + 8px);left:0;margin-top:0;box-shadow:0 20px 42px -18px rgba(16,24,40,.3);animation:nvd-in .17s}
.nvd .dropdown-menu::before{content:"";position:absolute;left:0;right:0;bottom:100%;height:10px} /* bridges the gap so the pointer can reach the menu */
@keyframes nvd-in{from{opacity:0;translate:0 -6px}}
@media (hover:hover){
  .nvd .dropdown:hover:not([data-hover-off])>.dropdown-menu{display:block}
  .nvd .dropdown:hover:not([data-hover-off])>.nav-link{background:#f2f4f8;color:#101828}
  .nvd .dropdown:hover:not([data-hover-off])>.nav-link svg{transform:rotate(180deg)}
}
.nvd .dropdown-header{margin:0;padding-bottom:5px;font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.nvd .dropdown-item{line-height:normal}
.nvd .nvd-cta{--bs-btn-padding-x:20px;--bs-btn-padding-y:10px;--bs-btn-font-size:.88rem;--bs-btn-font-weight:700;--bs-btn-line-height:normal;--bs-btn-border-radius:9px;--bs-btn-border-width:0;
  --bs-btn-color:#fff;--bs-btn-bg:#4f46e5;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#4338ca;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4338ca;margin-left:auto}
.nvd a:focus-visible,.nvd button:focus-visible{outline:2px solid #4f46e5;outline-offset:2px;box-shadow:none}
.nvd .dropdown-item:focus-visible{outline-offset:-2px}
@media (max-width:780px){
  .nvd{--bs-navbar-padding-x:16px;--bs-navbar-padding-y:10px;--bs-navbar-brand-margin-end:8px;flex-wrap:wrap;height:auto;row-gap:4px}
  .nvd .navbar-nav{order:2;width:100%;flex-wrap:wrap;gap:2px}
  .nvd .dropdown{position:static}
  .nvd .dropdown-menu{left:16px;right:16px;--bs-dropdown-min-width:0}
  .nvd .dropdown-menu::before{height:20px}
}
@media (prefers-reduced-motion:reduce){.nvd .dropdown-menu{animation:none}.nvd .nav-link,.nvd .nav-link svg{transition:none}}
JavaScript
/* Hover opening is CSS only, so Esc (or closing a menu with a click while the pointer is on it) sets data-hover-off
   to keep that menu shut until the pointer leaves. */
document.querySelectorAll('.nvd .dropdown').forEach(li => {
  li.addEventListener('pointerleave', () => li.removeAttribute('data-hover-off'));
  li.addEventListener('show.bs.dropdown', () => li.removeAttribute('data-hover-off'));
  li.addEventListener('hidden.bs.dropdown', () => { if (li.matches(':hover')) li.setAttribute('data-hover-off', ''); });
});
document.addEventListener('keydown', e => {
  if (e.key === 'Escape') document.querySelectorAll('.nvd .dropdown:hover').forEach(li => li.setAttribute('data-hover-off', ''));
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<nav class="nvd relative flex h-16 w-full items-center gap-1.5 border-b border-nvd-line bg-white px-[30px] max-[780px]:h-auto max-[780px]:flex-wrap max-[780px]:gap-y-1 max-[780px]:px-4 max-[780px]:py-2.5" aria-label="Main">
  <a href="#" class="mr-[22px] text-[1.05rem] font-extrabold tracking-[-.02em] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nvd-accent max-[780px]:mr-2">Northwind</a>
  <ul class="flex items-center gap-1.5 max-[780px]:order-2 max-[780px]:w-full max-[780px]:flex-wrap max-[780px]:gap-0.5">
    <li class="group/item relative max-[780px]:static">
      <button class="peer group/btn flex cursor-pointer items-center gap-1.5 rounded-[9px] px-3.5 py-2.5 text-[.9rem] font-semibold text-nvd-text transition-colors duration-150 group-hover/item:bg-nvd-soft group-hover/item:text-nvd-ink group-data-[hover-off]/item:bg-transparent! group-data-[hover-off]/item:text-nvd-text! hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nvd-accent aria-expanded:bg-nvd-soft! aria-expanded:text-nvd-ink! motion-reduce:transition-none" type="button" aria-expanded="false" aria-controls="nvd-products">Products <svg class="size-[13px] fill-none stroke-current stroke-[2.4] transition-transform duration-200 group-hover/item:rotate-180 group-data-[hover-off]/item:rotate-0! group-aria-expanded/btn:rotate-180! motion-reduce:transition-none" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      <div class="invisible absolute top-[calc(100%+8px)] left-0 z-10 min-w-[230px] -translate-y-1.5 rounded-[13px] border border-nvd-line bg-white p-[7px] opacity-0 shadow-[0_20px_42px_-18px_rgba(16,24,40,.3)] transition-[opacity,translate,visibility] duration-[170ms] before:absolute before:inset-x-0 before:bottom-full before:h-2.5 before:content-[''] group-hover/item:visible group-hover/item:translate-y-0 group-hover/item:opacity-100 group-data-[hover-off]/item:invisible! group-data-[hover-off]/item:opacity-0! peer-aria-expanded:visible peer-aria-expanded:translate-y-0 peer-aria-expanded:opacity-100 motion-reduce:transition-none max-[780px]:right-4 max-[780px]:left-4 max-[780px]:min-w-0 max-[780px]:before:h-5" id="nvd-products">
        <p class="px-3 pt-[9px] pb-[5px] text-[.7rem] font-bold tracking-[.08em] text-[#667085] uppercase" id="nvd-h-platform">Platform</p>
        <ul aria-labelledby="nvd-h-platform">
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">Dashboard</a></li>
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">Automations</a></li>
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">Integrations</a></li>
        </ul>
        <p class="px-3 pt-[9px] pb-[5px] text-[.7rem] font-bold tracking-[.08em] text-[#667085] uppercase" id="nvd-h-addons">Add-ons</p>
        <ul aria-labelledby="nvd-h-addons">
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">Analytics</a></li>
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">Audit log</a></li>
        </ul>
      </div>
    </li>
    <li class="group/item relative max-[780px]:static">
      <button class="peer group/btn flex cursor-pointer items-center gap-1.5 rounded-[9px] px-3.5 py-2.5 text-[.9rem] font-semibold text-nvd-text transition-colors duration-150 group-hover/item:bg-nvd-soft group-hover/item:text-nvd-ink group-data-[hover-off]/item:bg-transparent! group-data-[hover-off]/item:text-nvd-text! hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nvd-accent aria-expanded:bg-nvd-soft! aria-expanded:text-nvd-ink! motion-reduce:transition-none" type="button" aria-expanded="false" aria-controls="nvd-solutions">Solutions <svg class="size-[13px] fill-none stroke-current stroke-[2.4] transition-transform duration-200 group-hover/item:rotate-180 group-data-[hover-off]/item:rotate-0! group-aria-expanded/btn:rotate-180! motion-reduce:transition-none" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      <div class="invisible absolute top-[calc(100%+8px)] left-0 z-10 min-w-[230px] -translate-y-1.5 rounded-[13px] border border-nvd-line bg-white p-[7px] opacity-0 shadow-[0_20px_42px_-18px_rgba(16,24,40,.3)] transition-[opacity,translate,visibility] duration-[170ms] before:absolute before:inset-x-0 before:bottom-full before:h-2.5 before:content-[''] group-hover/item:visible group-hover/item:translate-y-0 group-hover/item:opacity-100 group-data-[hover-off]/item:invisible! group-data-[hover-off]/item:opacity-0! peer-aria-expanded:visible peer-aria-expanded:translate-y-0 peer-aria-expanded:opacity-100 motion-reduce:transition-none max-[780px]:right-4 max-[780px]:left-4 max-[780px]:min-w-0 max-[780px]:before:h-5" id="nvd-solutions">
        <ul>
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">For startups</a></li>
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">For agencies</a></li>
          <li><a class="block rounded-[9px] px-3 py-[9px] text-[.87rem] text-nvd-text hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nvd-accent" href="#">For enterprise</a></li>
        </ul>
      </div>
    </li>
    <li><a class="flex items-center rounded-[9px] bg-nvd-soft px-3.5 py-2.5 text-[.9rem] font-semibold text-nvd-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nvd-accent" href="#" aria-current="page">Pricing</a></li>
    <li><a class="flex items-center rounded-[9px] px-3.5 py-2.5 text-[.9rem] font-semibold text-nvd-text transition-colors duration-150 hover:bg-nvd-soft hover:text-nvd-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nvd-accent motion-reduce:transition-none" href="#">Docs</a></li>
  </ul>
  <a href="#" class="ml-auto rounded-[9px] bg-nvd-accent px-5 py-2.5 text-[.88rem] font-bold text-white hover:bg-[#4338ca] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nvd-accent">Start free</a>
</nav>
CSS
@theme {
  --color-nvd-ink: #101828;
  --color-nvd-text: #475467;
  --color-nvd-soft: #f2f4f8;
  --color-nvd-line: #e7e9f0;
  --color-nvd-accent: #4f46e5;
}
JavaScript
/* Disclosure navigation (WAI-ARIA pattern). Buttons toggle aria-expanded; opening one closes the others.
   Esc closes the open submenu (focus returns to its button) or dismisses a hover-opened one; a click outside
   or tabbing away also closes it. data-hover-off keeps a dismissed menu shut until the pointer leaves. */
document.querySelectorAll('.nvd').forEach(nav => {
  const btns = [...nav.querySelectorAll('button[aria-controls]')];
  const isOpen = b => b.getAttribute('aria-expanded') === 'true';
  const set = (b, open) => {
    b.setAttribute('aria-expanded', open);
    b.parentElement.toggleAttribute('data-hover-off', !open && b.parentElement.matches(':hover'));
  };
  btns.forEach(b => {
    const li = b.parentElement;
    b.addEventListener('click', () => { const open = !isOpen(b); btns.forEach(x => { if (x !== b && isOpen(x)) set(x, false); }); set(b, open); });
    li.addEventListener('focusout', e => { if (e.relatedTarget && !li.contains(e.relatedTarget) && isOpen(b)) set(b, false); });
    li.addEventListener('pointerleave', () => li.removeAttribute('data-hover-off'));
  });
  document.addEventListener('click', e => btns.forEach(b => { if (isOpen(b) && !b.parentElement.contains(e.target)) set(b, false); }));
  document.addEventListener('keydown', e => {
    if (e.key !== 'Escape') return;
    btns.forEach(b => {
      const li = b.parentElement;
      if (isOpen(b)) { const inside = li.contains(document.activeElement); set(b, false); if (inside) b.focus(); }
      else if (li.matches(':hover')) li.setAttribute('data-hover-off', '');
    });
  });
});

About this set

A white top navigation bar with a logo, two dropdowns, two plain links and an indigo Start free button on the right. Products opens a panel with links under Platform and Add-ons headings; Solutions opens a short list of audiences. Pricing is marked as the current page. Use it for marketing sites and product pages where a few sections need a second level. With a mouse the dropdowns open on hover, but hover alone would leave keyboard and touch users out, so every top-level item is also a disclosure button with aria-expanded that opens by click, tap, Enter or Space. Esc closes the open dropdown and returns focus to its button, and it also dismisses a menu that opened on hover. An invisible bridge covers the gap under each button so the pointer can travel into the panel without it closing. Hover effects only apply on devices that can hover, so phones get a clean tap to open and tap to close. Below 780 pixels the links move to a second row and a dropdown opens full width under the bar. The Bootstrap version uses .navbar with the Dropdown plugin.

What’s included

  • Dropdowns that open on hover, click, tap or keyboard
  • Esc closes a clicked or a hovered menu
  • Pointer bridge so the panel stays open on the way down
  • Grouped links with small uppercase headings
  • Links wrap to a second row below 780px

Accessibility

The bar is a nav labelled Main. Each dropdown opens from a button with aria-expanded and aria-controls, Esc closes it and returns focus to the button, and moving focus out or clicking elsewhere closes it too. Link groups are lists named by their headings and Pricing carries aria-current="page". Everything focusable shows a 2px indigo outline, and transitions are removed under prefers-reduced-motion.

Customise it

Change the link, hover and accent colours in .nvd-top, .nvd-drop and .nvd-cta (plain CSS), the --bs-navbar-* and --bs-dropdown-* variables on .nvd (Bootstrap) or the --color-nvd-* theme colours (Tailwind). The wrap point is the 780px media query.