Navigation and Menus

Dropdown Navigation Menu

Responsive navigation that balances desktop hover and mobile tap interactions.

HTMLCSSJavaScriptNavigation
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Dropdown Navigation Menu</title>
  <style>
    * { box-sizing: border-box; }
    body { min-height: 100vh; margin: 0; color: #1d2a25; font-family: "Avenir Next", "Microsoft YaHei", sans-serif; background: #f7f5ef; }
    nav { position: relative; z-index: 5; display: flex; align-items: center; gap: 28px; width: min(1160px, calc(100% - 36px)); margin: 18px auto 0; padding: 12px 16px 12px 22px; border: 1px solid #d9ded7; border-radius: 18px; background: rgba(255,255,255,.92); box-shadow: 0 16px 40px rgba(42,64,55,.08); }
    .brand { color: #1d2a25; font: 800 22px/1 Georgia, serif; text-decoration: none; letter-spacing: -.04em; }
    .brand i { color: #e96545; font-style: normal; }
    .menu { display: flex; align-items: center; gap: 6px; margin-left: auto; padding: 0; list-style: none; }
    .menu > li { position: relative; }
    .menu a, .drop-trigger { display: flex; align-items: center; gap: 7px; padding: 11px 13px; border: 0; border-radius: 10px; color: #48554f; font: inherit; font-size: 14px; font-weight: 700; text-decoration: none; background: transparent; cursor: pointer; }
    .menu a:hover, .drop-trigger:hover, .drop-trigger[aria-expanded="true"] { color: #1d2a25; background: #f0f4ef; }
    .drop-trigger::after { content: "⌄"; color: #85928c; transition: transform .2s ease; }
    .drop-trigger[aria-expanded="true"]::after { transform: rotate(180deg); }
    .dropdown { position: absolute; top: calc(100% + 10px); left: 0; width: 270px; padding: 10px; border: 1px solid #dce1dc; border-radius: 15px; background: #fff; box-shadow: 0 20px 50px rgba(42,64,55,.16); opacity: 0; visibility: hidden; transform: translateY(-8px); transition: .2s ease; }
    .drop-trigger[aria-expanded="true"] + .dropdown { opacity: 1; visibility: visible; transform: none; }
    .dropdown a { align-items: flex-start; padding: 12px; }
    .dropdown b { display: block; color: #27352f; font-size: 13px; }
    .dropdown small { display: block; margin-top: 3px; color: #84908a; font-weight: 500; line-height: 1.4; }
    .nav-action { margin-left: 4px; padding: 11px 17px; border: 0; border-radius: 10px; color: white; font: inherit; font-weight: 800; background: #e96545; cursor: pointer; }
    .menu-toggle { display: none; margin-left: auto; width: 42px; height: 42px; border: 1px solid #dce1dc; border-radius: 10px; color: #27352f; font-size: 20px; background: white; cursor: pointer; }
    button:focus-visible, a:focus-visible { outline: 3px solid #e96545; outline-offset: 2px; }
    main { width: min(1100px, calc(100% - 40px)); margin: 0 auto; min-height: calc(100vh - 100px); display: grid; place-items: center; text-align: center; }
    .hero { max-width: 760px; }
    .eyebrow { color: #e96545; font-size: 12px; font-weight: 900; letter-spacing: .19em; }
    h1 { margin: 14px 0 18px; font: 700 clamp(44px, 9vw, 88px)/.95 Georgia, "STSong", serif; letter-spacing: -.05em; }
    .hero p { max-width: 600px; margin: auto; color: #6b7771; font-size: 17px; line-height: 1.8; }
    @media (min-width: 761px) { .has-dropdown:hover .dropdown { opacity: 1; visibility: visible; transform: none; } }
    @media (max-width: 760px) {
      nav { flex-wrap: wrap; }
      .menu-toggle { display: block; }
      .menu { display: none; flex: 1 0 100%; align-items: stretch; flex-direction: column; margin: 0; padding-top: 8px; border-top: 1px solid #e8ebe7; }
      .menu.is-open { display: flex; }
      .menu a, .drop-trigger { width: 100%; justify-content: space-between; }
      .dropdown { position: static; display: none; width: 100%; margin-top: 4px; box-shadow: none; opacity: 1; visibility: visible; transform: none; }
      .drop-trigger[aria-expanded="true"] + .dropdown { display: block; }
      .nav-action { width: 100%; margin: 4px 0 0; }
    }
    @media (prefers-reduced-motion: reduce) { .dropdown, .drop-trigger::after { transition: none; } }
  </style>
</head>
<body>
  <nav aria-label="Primary navigation">
    <a class="brand" href="#">North<i>·</i>Lab</a>
    <button id="menuToggle" class="menu-toggle" type="button" aria-expanded="false" aria-controls="mainMenu" aria-label="Expand navigation">☰</button>
    <ul id="mainMenu" class="menu">
      <li><a href="#work">Works</a></li>
      <li class="has-dropdown"><button class="drop-trigger" type="button" aria-expanded="false">Services</button><div class="dropdown"><a href="#strategy"><span><b>Brand Strategy</b><small>Find the truly unique way your brand expresses itself</small></span></a><a href="#design"><span><b>Digital Products</b><small>Complete experience design from prototype to launch</small></span></a><a href="#motion"><span><b>Dynamic Visuals</b><small>Tell the product story through rhythm and motion</small></span></a></div></li>
      <li><a href="#about">About</a></li>
      <li><a href="#journal">Notes</a></li>
      <li><button class="nav-action" type="button">Start collaborating</button></li>
    </ul>
  </nav>
  <main><section class="hero"><span class="eyebrow">INDEPENDENT DESIGN STUDIO</span><h1>Complex Problems, Simple Expression</h1><p>This navigation supports hover and click on desktop, and automatically turns into an expandable menu on small screens. Try resizing the window to see the changes.</p></section></main>
  <script>
    const menuToggle = document.querySelector('#menuToggle');
    const menu = document.querySelector('#mainMenu');
    const dropTrigger = document.querySelector('.drop-trigger');
    menuToggle.addEventListener('click', () => {
      const open = menuToggle.getAttribute('aria-expanded') !== 'true';
      menuToggle.setAttribute('aria-expanded', String(open));
      menu.classList.toggle('is-open', open);
    });
    dropTrigger.addEventListener('click', () => dropTrigger.setAttribute('aria-expanded', String(dropTrigger.getAttribute('aria-expanded') !== 'true')));
    document.addEventListener('click', event => {
      if (!event.target.closest('.has-dropdown')) dropTrigger.setAttribute('aria-expanded', 'false');
    });
  </script>
</body>
</html>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback