DEV3 Technologies home

Sticky navbar with mobile drawer

A sticky top navigation that collapses into a slide-in drawer on small screens, with Escape to close and a backdrop.

Tailwind CSSNavigationnavbardrawerresponsivesticky

Live preview

Code

<header class="sticky top-0 z-40 border-b border-slate-200 bg-white/80 backdrop-blur">
  <nav class="mx-auto flex h-16 max-w-6xl items-center justify-between px-4" aria-label="Main">
    <a href="/" class="text-lg font-bold text-slate-900">Acme</a>
    <ul class="hidden items-center gap-8 text-sm font-medium text-slate-600 md:flex">
      <li><a class="hover:text-slate-900" href="#features">Features</a></li>
      <li><a class="hover:text-slate-900" href="#pricing">Pricing</a></li>
      <li><a class="hover:text-slate-900" href="#faq">FAQ</a></li>
      <li><a class="rounded-lg bg-indigo-600 px-4 py-2 text-white" href="#start">Get started</a></li>
    </ul>
    <button id="nav-open" type="button" class="md:hidden" aria-controls="nav-drawer" aria-expanded="false">
      <span class="sr-only">Open menu</span>
      <svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7h16M4 12h16M4 17h16"/></svg>
    </button>
  </nav>
</header>

<div id="nav-backdrop" class="fixed inset-0 z-40 hidden bg-slate-900/50 md:hidden"></div>
<aside id="nav-drawer" inert
  class="fixed inset-y-0 left-0 z-50 w-72 -translate-x-full bg-white p-6 shadow-xl transition-transform md:hidden">
  <button id="nav-close" type="button" class="mb-6 text-sm text-slate-500">Close</button>
  <ul class="space-y-4 text-lg font-medium text-slate-800">
    <li><a href="#features">Features</a></li>
    <li><a href="#pricing">Pricing</a></li>
    <li><a href="#faq">FAQ</a></li>
    <li><a href="#start" class="text-indigo-600">Get started</a></li>
  </ul>
</aside>

How it works

What it solves

A site header has to stay reachable while scrolling, show every link on wide screens, and turn into a usable menu on phones without a heavy framework. This pattern does all of that with one header, one drawer and about twenty lines of script.

How it works

The header uses sticky top-0 with a translucent background and backdrop-blur, so content slides underneath it. The desktop links live in a hidden md:flex list. On small screens a menu button toggles the drawer, which is a fixed panel moved with -translate-x-full and translate-x-0. The button keeps aria-expanded in sync and points to the drawer through aria-controls. A sibling backdrop closes the drawer on click, and a keydown listener closes it on Escape. While open, the page body gets overflow-hidden so the background does not scroll.

Adapting it

Swap the links in both lists (keep them in one JavaScript array if you generate markup). To open from the right, change the left anchor to right and flip the translate sign. Raise the breakpoint from md to lg for long menus.

Pitfalls

  • A sticky header needs a parent without overflow: hidden, or it silently stops sticking.
  • Return focus to the menu button after closing so keyboard users keep their place.
  • Use inert or hidden on the closed drawer if it contains focusable links.

How to use it

  1. Paste navbar.html as the first element inside <body> of a Tailwind 4 page.
  2. Load navbar.js with defer at the end of the page.
  3. Replace the Acme logo text and the link list (desktop and drawer copies).
  4. Give the main content enough height to see the sticky behaviour.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets