Building a Responsive Navbar with a Mobile Menu

Intermediate
13 min

Building a Responsive Navbar with a Mobile Menu

The navigation bar is where layout, breakpoints, states and accessibility first meet. In this lesson you will assemble one from the utilities covered so far: a sticky, translucent header; a desktop link row with an active state; a hover-and-keyboard dropdown; and a mobile menu that needs no JavaScript.

The shell: sticky, translucent, constrained

Start with a header that stays visible while scrolling and a nav centred at a maximum width:

html
<header class="sticky top-0 z-40 border-b border-gray-200 bg-white/90 backdrop-blur"> <nav class="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6"> <a href="/" class="text-lg font-bold tracking-tight">Acme</a> <!-- links and actions go here --> </nav> </header>

sticky top-0 z-40 keeps the bar above page content, bg-white/90 backdrop-blur lets content show faintly through as it scrolls under, h-16 fixes the height so the layout never jumps, and justify-between separates logo, links and actions.

Desktop links and the active state

The link list is hidden on small screens and becomes a flex row from md upward. The current page carries aria-current="page", which announces it to assistive technology and doubles as the styling hook:

html
<ul class="hidden items-center gap-6 md:flex"> <li> <a href="/docs" aria-current="page" class="text-sm font-medium text-gray-600 transition-colors hover:text-gray-900 aria-[current=page]:text-blue-600">Docs</a> </li> <li><a href="/pricing" class="text-sm font-medium text-gray-600 hover:text-gray-900">Pricing</a></li> </ul>

A second hidden md:flex group on the right holds "Sign in" and a primary button. Because the active style comes from the attribute, a template only sets aria-current and the visuals follow.

A dropdown that works with keyboard and mouse

A "Products" menu opens on hover for mouse users and on focus for keyboard users, both handled by group variants on a wrapper:

html
<li class="group relative"> <button class="flex items-center gap-1 text-sm font-medium text-gray-600" aria-haspopup="true"> Products <svg class="size-4 transition-transform group-hover:rotate-180" viewBox="0 0 20 20">...</svg> </button> <div class="invisible absolute left-0 top-full w-56 rounded-lg border bg-white p-2 opacity-0 shadow-lg transition group-hover:visible group-hover:opacity-100 group-focus-within:visible group-focus-within:opacity-100"> <a class="block rounded-md px-3 py-2 text-sm hover:bg-gray-50" href="/analytics">Analytics</a> </div> </li>

invisible plus opacity-0 hides the panel without removing it, so it can fade in and focus-within can reveal it for keyboard users. top-full makes the panel touch the trigger, so the menu does not close as the pointer moves down.

The mobile menu without JavaScript

On small screens a <details> element takes over; the browser manages open state and keyboard toggling, and Tailwind styles it with group-open::

html
<details class="group relative md:hidden"> <summary class="list-none cursor-pointer rounded-md p-2 hover:bg-gray-100 focus-visible:ring-2"> <span class="sr-only">Toggle menu</span> <svg class="size-6 group-open:hidden">…hamburger…</svg> <svg class="hidden size-6 group-open:block">…close…</svg> </summary> <ul class="absolute inset-x-0 top-full mt-2 space-y-1 border-b bg-white p-4 shadow-lg"> <li><a class="block rounded-md px-3 py-2 hover:bg-gray-50" href="/docs">Docs</a></li> </ul> </details>

list-none removes the disclosure triangle, sr-only labels the control for screen readers, and swapping two icons with group-open: gives a clear affordance. If the panel must close when a link is tapped, a few lines of JavaScript that remove the open attribute are enough.

Common mistakes

  • Building the dropdown with hidden/group-hover:block; display cannot transition, and keyboard users never see it without focus-within.
  • Marking the active link with a hard-coded class instead of aria-current, which screen readers cannot use.
Quick Quiz
Question 1 of 2

Which attribute both announces the current page to assistive technology and drives the active-link style?

Key Takeaways

  • A sticky, translucent header uses sticky top-0 z-40 bg-white/90 backdrop-blur with a fixed height.
  • Show the link row with hidden md:flex and style the current page via aria-[current=page]:.
  • Dropdowns need group-hover: and group-focus-within: together, with invisible opacity-0 so they can animate.
  • <details>/<summary> plus group-open: gives a mobile menu with keyboard support and no JavaScript.
  • Reuse the page's max-w-7xl px-4 container so edges align.

Next lesson: Building Cards, Buttons and Badges — design the small reusable pieces that every page is assembled from.

Building a Responsive Navbar with a Mobile Menu - Tailwind CSS | CodeYourCraft | CodeYourCraft