A Navbar You Can Use Right Away

The navbar is a fixture on almost every website, and it's also the piece most likely to get reworked during a responsive refit: the desktop menu looks fine, then on mobile it either crams into a mess or can only be "used" through browser zoom. The component below bundles the common requirements in one shot — sticky positioning, a mobile hamburger menu, a dropdown submenu, active-page highlighting, plus a CTA button and accessibility labels. Copy it and it works.

It has zero framework dependencies — plain HTML/CSS/JavaScript — so it drops into any project. It suits three typical site types: content blogs (simple menu, reading-first), corporate sites (group services under a "Services" dropdown), and conversion-oriented landing pages (a CTA button that drives inquiries). Different sites only need adjustments to the menu structure and highlight logic; the layout skeleton can be reused. When accepting the result end to end, walk through the responsive layout acceptance checklist item by item.

HTML

<nav class="navbar">
  <div class="nav-container">
    <a href="/" class="nav-logo">
      <img src="logo.svg" alt="Logo" height="32">
    </a>

    <button class="nav-toggle" aria-label="Toggle menu">
      <span></span>
      <span></span>
      <span></span>
    </button>

    <ul class="nav-menu">
      <li><a href="/" class="active">Home</a></li>
      <li class="dropdown">
        <a href="/services">Services ▾</a>
        <ul class="dropdown-menu">
          <li><a href="/web-design">Web Design</a></li>
          <li><a href="/hosting">Hosting</a></li>
          <li><a href="/seo">SEO</a></li>
        </ul>
      </li>
      <li><a href="/about">About</a></li>
      <li><a href="/blog">Blog</a></li>
      <li><a href="/contact" class="nav-cta">Contact</a></li>
    </ul>
  </div>
</nav>

CSS

/* Base styles */
.navbar {
  background: #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,.1);
  position: sticky;
  top: 0;
  z-index: 1000;
}

.nav-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
}

.nav-menu {
  display: flex;
  list-style: none;
  gap: 8px;
  margin: 0;
  padding: 0;
}

.nav-menu a {
  text-decoration: none;
  color: #333;
  padding: 8px 16px;
  border-radius: 6px;
  transition: background .2s;
}

.nav-menu a:hover,
.nav-menu a.active {
  background: #f0f0f0;
  color: #4F46E5;
}

.nav-cta {
  background: #4F46E5;
  color: #fff !important;
}

.nav-cta:hover {
  background: #4338CA !important;
}

/* Dropdown submenu */
.dropdown { position: relative; }

.dropdown-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background: #fff;
  box-shadow: 0 4px 12px rgba(0,0,0,.1);
  border-radius: 8px;
  min-width: 180px;
  list-style: none;
  padding: 8px;
}

.dropdown:hover .dropdown-menu {
  display: block;
}

/* Hamburger button */
.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
}

.nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background: #333;
  transition: transform .3s;
}

/* Mobile */
@media (max-width: 768px) {
  .nav-toggle { display: flex; }

  .nav-menu {
    display: none;
    position: absolute;
    top: 64px;
    left: 0;
    right: 0;
    background: #fff;
    flex-direction: column;
    padding: 16px;
    box-shadow: 0 4px 12px rgba(0,0,0,.1);
  }

  .nav-menu.active { display: flex; }

  .dropdown-menu {
    position: static;
    box-shadow: none;
    padding-left: 16px;
  }
}

JavaScript

document.addEventListener('DOMContentLoaded', () => {
  const toggle = document.querySelector('.nav-toggle');
  const menu = document.querySelector('.nav-menu');

  toggle.addEventListener('click', () => {
    menu.classList.toggle('active');
    toggle.setAttribute('aria-expanded',
      toggle.getAttribute('aria-expanded') === 'false' ? 'true' : 'false'
    );
  });

  // Close when clicking outside the navbar
  document.addEventListener('click', (e) => {
    if (!e.target.closest('.navbar')) {
      menu.classList.remove('active');
    }
  });
});

Features

  • ✅ Sticky header
  • ✅ Mobile hamburger toggle
  • ✅ Dropdown submenu
  • ✅ Active page highlighting
  • ✅ Click outside to close
  • ✅ ARIA accessibility labels
  • ✅ CTA button styling

Integration Notes

Three details matter when you wire this into a real project. First, replace the logo path and menu links with your actual URLs. Second, add the .active class to the menu item for the current page — highlighting is pure CSS, no JavaScript needed. Third, if you're already on Bootstrap or Tailwind, keep only the interaction logic and swap the styles for framework utility classes.

If the menu is rendered server-side — for example with Next.js or Vue 3 — render the .active class directly from the current route so the highlight survives a refresh.

Extensions & Customization

Once the skeleton is in place, three common extensions cover most sites:

Multi-level dropdowns. When services go deeper than one level, nest another <ul> inside .dropdown-menu and float the second level horizontally. Remember to revert multi-level dropdowns to vertical stacking inside the mobile breakpoint, or they become nearly impossible to use on touchscreens.

Search box. Put the search form inside .nav-container, hidden by default on mobile, expanded when the search icon is tapped. Use min(320px, 100%) for the input width so it never overflows on small screens.

Dark mode. Drive the background and text colors with CSS variables:

.navbar { background: var(--nav-bg, #fff); }
@media (prefers-color-scheme: dark) {
  :root { --nav-bg: #1f2937; --nav-text: #f9fafb; }
}

Scroll feedback. Collapse the nav while scrolling down and reveal it on scroll up — it frees reading space and avoids the position: sticky placeholder on long pages. Throttle such interactions with requestAnimationFrame to avoid frequent layout reads and writes.

For visual details like type, spacing, and icons, let the design system decide: see design system components and modern CSS layout.

Accessibility Details

  • aria-expanded updates with the menu state, so screen readers can sense whether the menu is open;
  • The aria-label="Toggle menu" on the hamburger button describes its purpose;
  • The dropdown currently relies on hover; add :focus-within so keyboard users can open submenus too:
.dropdown:focus-within .dropdown-menu { display: block; }
  • After the mobile menu opens, move focus to the first menu item so keyboard users don't get "lost" on the page. For the full set of practices, see the web accessibility guide.

Performance & Compatibility Tips

  • Inline critical CSS on key pages so the nav doesn't flash before styles load (FOUC);
  • Use inline SVG or iconfont for icons — one less request than a single image;
  • With 8 or fewer menu items, skip a JS framework; vanilla code is enough;
  • For legacy browsers (e.g., IE), add @supports or fallback styles; all modern browsers support flex.

FAQ

Why position: sticky instead of fixed? When the nav is at the top of the page, the two behave the same — but sticky keeps its own space in the document flow. When the nav isn't the first element on the page, fixed pulls the content up underneath it and causes overlap; sticky doesn't.

Dropdown won't open on touchscreens? The default relies on hover; on touch the first tap triggers hover instead of navigation. Switch the dropdown to tap-to-toggle inside the mobile breakpoint, or render submenus as a static list.

Too many top-level items? Above 6-7 items, horizontal space gets tight. A common fix is to fold secondary entries into a "More" dropdown, or use icon-plus-label combinations to compress width.

What if another sticky element (like a promo bar) sits above the nav? Give .navbar a top equal to that element's height (e.g., top: 40px) and the nav will stick below the bar. When multiple sticky elements coexist, sort them with z-index.

Tapping the hamburger doesn't open the menu? Most likely the JS didn't load, or the .nav-toggle/.nav-menu class names were overridden by a framework. Open the console to confirm there are no errors, then check that the selector in menu.classList.toggle('active') matches the actual DOM.

Reference: MDN <nav> element — https://developer.mozilla.org/docs/Web/HTML/Element/nav ; WAI-ARIA Menu Pattern — https://www.w3.org/WAI/ARIA/apg/patterns/menu/