Navegación y menús

Menú desplegable de navegación

Navegación responsiva que combina el hover en escritorio y el toque en móviles.

HTMLCSSJavaScriptNavegación
Tras activar JavaScript, puedes editar, ejecutar y guardar este ejemplo. El código fuente fijado es el siguiente:
<!doctype html>
<html lang="es" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Menú desplegable de navegación</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="navegación principal">
    <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="Navegación desplegable">☰</button>
    <ul id="mainMenu" class="menu">
      <li><a href="#work">Obras</a></li>
      <li class="has-dropdown"><button class="drop-trigger" type="button" aria-expanded="false">Servicios</button><div class="dropdown"><a href="#strategy"><span><b>Estrategia de marca</b><small>Encontrar la expresión verdaderamente única de la marca</small></span></a><a href="#design"><span><b>Productos digitales</b><small>Una experiencia de diseño integral, del prototipo al lanzamiento</small></span></a><a href="#motion"><span><b>Visual dinámico</b><small>Cuenta la historia del producto con ritmo y movimiento</small></span></a></div></li>
      <li><a href="#about">Acerca de</a></li>
      <li><a href="#journal">Apuntes</a></li>
      <li><button class="nav-action" type="button">Empezar a colaborar</button></li>
    </ul>
  </nav>
  <main><section class="hero"><span class="eyebrow">INDEPENDENT DESIGN STUDIO</span><h1>Problemas complejos, expresión sencilla</h1><p>Esta navegación admite pasar el cursor y hacer clic en escritorio, y en pantallas pequeñas se convierte automáticamente en un menú desplegable. Prueba a cambiar el tamaño de la ventana para ver el cambio.</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>
Comentarios de la página¿Has encontrado un problema o tienes sugerencias de mejora?
Las funciones de retroalimentación deben usarse en la versión en línea

Envía tras activar JavaScript en la versión en línea; las funciones locales de la herramienta o del ejemplo no se verán afectadas.

Ir a la versión en línea para dar feedback