Ad – 728×90
🛠️ CSS Projects

CSS Responsive Navigation – Hamburger Menu & Navbar

Navigation is the spine of every website. A good nav must work on all screen sizes, be keyboard-navigable, have clear hover and active states, and not require a JavaScript framework. This project builds a complete responsive navigation system from scratch: a desktop horizontal navbar with dropdowns, a mobile hamburger menu, sticky behavior, and full accessibility attributes.

⏱️ 35 min 🎯 Intermediate 📅 Updated 2026

HTML Structure

Start with semantic HTML. The <nav> element with aria-label makes the navbar accessible to screen readers. The hamburger button needs aria-expanded toggled by JavaScript.

HTML – Navigation structure
<header class="navbar" id="navbar">
  <a href="/" class="navbar__logo" aria-label="Home">
    <img src="/logo.png" alt="ylearner" width="120" height="36">
  </a>

  <!-- Desktop nav links -->
  <nav class="navbar__nav" aria-label="Main navigation">
    <ul class="navbar__list" role="list">
      <li><a href="/" class="navbar__link navbar__link--active">Home</a></li>
      <li class="navbar__item--dropdown">
        <button class="navbar__link navbar__dropdown-toggle" aria-expanded="false">
          Courses ▾
        </button>
        <ul class="navbar__dropdown" role="list">
          <li><a href="/python/" class="navbar__dropdown-item">🐍 Python</a></li>
          <li><a href="/csslessons/" class="navbar__dropdown-item">🎨 CSS</a></li>
          <li><a href="/html/" class="navbar__dropdown-item">🌐 HTML</a></li>
        </ul>
      </li>
      <li><a href="/about.html" class="navbar__link">About</a></li>
    </ul>
  </nav>

  <a href="/python/what-is-python.html" class="navbar__cta">Start Learning →</a>

  <!-- Hamburger toggle -->
  <button class="navbar__hamburger" id="hamburger"
          aria-label="Toggle menu" aria-expanded="false"
          aria-controls="mobileMenu">
    <span></span><span></span><span></span>
  </button>
</header>

Base Navbar CSS

CSS – Navbar base
:root {
  --nav-height:  64px;
  --nav-bg:      #ffffff;
  --nav-text:    #1a1a1a;
  --nav-primary: #1572B6;
  --nav-border:  #e0e0e0;
}

.navbar {
  display: flex;
  align-items: center;
  gap: 2rem;
  height: var(--nav-height);
  padding: 0 2rem;
  background: var(--nav-bg);
  border-bottom: 1px solid var(--nav-border);
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 1px 8px rgba(0,0,0,.06);
}

.navbar__logo img { display: block; }

.navbar__nav { margin-right: auto; }  /* push cta to right */

.navbar__list {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: .25rem;
}

.navbar__link {
  display: flex;
  align-items: center;
  gap: .3rem;
  padding: .5rem .75rem;
  color: var(--nav-text);
  text-decoration: none;
  font-weight: 500;
  font-size: .95rem;
  border-radius: 6px;
  background: none;
  border: none;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.navbar__link:hover { background: #f0f0f0; color: var(--nav-primary); }
.navbar__link--active { color: var(--nav-primary); font-weight: 700; }

.navbar__cta {
  padding: .5rem 1.2rem;
  background: var(--nav-primary);
  color: #fff;
  border-radius: 6px;
  text-decoration: none;
  font-weight: 600;
  font-size: .9rem;
  white-space: nowrap;
  transition: background .2s, transform .2s;
}
.navbar__cta:hover { background: #0f5fa0; transform: translateY(-1px); }

.navbar__hamburger { display: none; }
Ad – 336×280
CSS – Dropdown
.navbar__item--dropdown { position: relative; }

.navbar__dropdown {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 180px;
  background: #fff;
  border: 1px solid var(--nav-border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.12);
  list-style: none;
  margin: 0;
  padding: .4rem 0;
  /* Hidden by default */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .15s, visibility .15s, transform .15s;
  z-index: 200;
}

/* Show on hover of parent li, or when toggle has aria-expanded=true */
.navbar__item--dropdown:hover .navbar__dropdown,
.navbar__item--dropdown:focus-within .navbar__dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.navbar__dropdown-item {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem 1rem;
  color: var(--nav-text);
  text-decoration: none;
  font-size: .9rem;
  transition: background .12s;
}
.navbar__dropdown-item:hover { background: #f5f5f5; color: var(--nav-primary); }

Hamburger Menu (Mobile)

CSS – Hamburger
.navbar__hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: .5rem;
  margin-left: auto;
}
.navbar__hamburger span {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--nav-text);
  border-radius: 2px;
  transition: all .25s ease;
}

/* Animate to X when open */
.navbar__hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.navbar__hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.navbar__hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 768px) {
  .navbar__hamburger { display: flex; }
  .navbar__nav, .navbar__cta { display: none; }

  /* Mobile menu — full-width dropdown below header */
  .navbar__nav.mobile-open {
    display: block;
    position: fixed;
    top: var(--nav-height);
    left: 0;
    right: 0;
    background: #fff;
    border-bottom: 1px solid var(--nav-border);
    box-shadow: 0 8px 24px rgba(0,0,0,.12);
    padding: 1rem;
    animation: slideDown .2s ease;
    z-index: 99;
  }
  .navbar__nav.mobile-open .navbar__list { flex-direction: column; gap: .25rem; }
  .navbar__nav.mobile-open .navbar__link { width: 100%; font-size: 1rem; padding: .65rem .75rem; }
  .navbar__nav.mobile-open .navbar__dropdown { position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none; border: none; padding-left: 1rem; }
}

@keyframes slideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

JavaScript Toggle

JS – Hamburger toggle
const hamburger = document.getElementById('hamburger');
const nav       = document.querySelector('.navbar__nav');

hamburger.addEventListener('click', () => {
  const open = hamburger.getAttribute('aria-expanded') === 'true';
  hamburger.setAttribute('aria-expanded', String(!open));
  nav.classList.toggle('mobile-open', !open);
});

// Close on outside click
document.addEventListener('click', (e) => {
  if (!e.target.closest('.navbar')) {
    hamburger.setAttribute('aria-expanded', 'false');
    nav.classList.remove('mobile-open');
  }
});

// Close on Escape key
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') {
    hamburger.setAttribute('aria-expanded', 'false');
    nav.classList.remove('mobile-open');
    hamburger.focus(); // return focus to toggle button
  }
});

📋 Key Patterns

  • Sticky header: position: sticky; top: 0; with z-index high enough to stay above content.
  • Dropdown: opacity + visibility + transform instead of display:none — allows CSS transitions.
  • Hamburger X animation: rotate and translate the three spans using aria-expanded as the state toggle.
  • Accessibility: use aria-expanded, aria-label, aria-controls; close on Escape; return focus to toggle.