Tarjetas, listas y organización de contenido

Panel acordeón

Componente de acordeón de preguntas frecuentes con operación mediante teclado y despliegue suave.

HTMLCSSJavaScriptaccesibilidad
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>Panel acordeón</title>
  <style>
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      padding: clamp(24px, 7vw, 76px) 20px;
      color: #18302a;
      font-family: "Gill Sans", "Microsoft YaHei", sans-serif;
      background: linear-gradient(135deg, #f4f0d8 0 55%, #d7ead5 55%);
    }
    main { width: min(820px, 100%); margin: auto; }
    header { display: grid; grid-template-columns: 1fr auto; gap: 24px; align-items: end; margin-bottom: 34px; }
    .kicker { color: #d5523f; font-size: 12px; font-weight: 800; letter-spacing: .2em; }
    h1 { margin: 8px 0 0; font: 800 clamp(36px, 8vw, 66px)/1 "STKaiti", "KaiTi", serif; }
    .leaf { width: 80px; height: 80px; border-radius: 80% 10% 80% 10%; background: #2e8169; transform: rotate(10deg); box-shadow: inset 14px 14px rgba(255,255,255,.16); }
    .accordion { border-top: 2px solid #18302a; }
    .item { border-bottom: 1px solid rgba(24, 48, 42, .35); }
    .trigger {
      width: 100%;
      display: grid;
      grid-template-columns: 48px 1fr 34px;
      gap: 12px;
      align-items: center;
      padding: 24px 4px;
      border: 0;
      color: inherit;
      text-align: left;
      font: inherit;
      font-size: 19px;
      font-weight: 750;
      background: transparent;
      cursor: pointer;
    }
    .index { color: #d5523f; font: 700 13px/1 Georgia, serif; }
    .plus { position: relative; width: 28px; height: 28px; border: 1px solid currentColor; border-radius: 50%; transition: transform .25s ease; }
    .plus::before, .plus::after { content: ""; position: absolute; left: 7px; right: 7px; top: 13px; height: 1px; background: currentColor; }
    .plus::after { transform: rotate(90deg); transition: opacity .2s ease; }
    .trigger[aria-expanded="true"] .plus { transform: rotate(90deg); color: #d5523f; }
    .trigger[aria-expanded="true"] .plus::after { opacity: 0; }
    .trigger:focus-visible { outline: 3px solid #d5523f; outline-offset: -3px; }
    .panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .3s ease; }
    .panel.is-open { grid-template-rows: 1fr; }
    .panel-content { overflow: hidden; }
    .panel p { max-width: 650px; margin: 0; padding: 0 48px 24px 64px; color: #4d625c; line-height: 1.8; }
    @media (max-width: 520px) { header { grid-template-columns: 1fr; } .leaf { display: none; } .panel p { padding-left: 4px; } }
    @media (prefers-reduced-motion: reduce) { .panel, .plus { transition: none; } }
  </style>
</head>
<body>
  <main>
    <header>
      <div><span class="kicker">GARDEN NOTES</span><h1>Preguntas frecuentes</h1></div>
      <div class="leaf" aria-hidden="true"></div>
    </header>
    <section class="accordion">
      <div class="item">
        <button id="trigger-1" class="trigger" type="button" aria-expanded="true" aria-controls="panel-1"><span class="index">01</span><span>¿Cómo elegir la ubicación de las plantas de interior?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-1" class="panel is-open" role="region" aria-labelledby="trigger-1" aria-hidden="false"><div class="panel-content"><p>Primero observa cómo cambia la luz natural a lo largo del día. La mayoría de las plantas de follaje prefieren una luz brillante pero indirecta; como punto de partida cómodo, suele funcionar situarse a uno o dos metros de una ventana orientada al sur.</p></div></div>
      </div>
      <div class="item">
        <button id="trigger-2" class="trigger" type="button" aria-expanded="false" aria-controls="panel-2"><span class="index">02</span><span>¿Cada cuánto conviene regar?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-2" class="panel" role="region" aria-labelledby="trigger-2" aria-hidden="true"><div class="panel-content"><p>No te limites a mirar el calendario. Introduce un dedo en la tierra unos dos centímetros; riega a fondo cuando la capa superficial ya esté seca. En invierno la evaporación es más lenta, así que conviene alargar el intervalo.</p></div></div>
      </div>
      <div class="item">
        <button id="trigger-3" class="trigger" type="button" aria-expanded="false" aria-controls="panel-3"><span class="index">03</span><span>¿Qué hacer si los bordes de las hojas se vuelven amarillos?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-3" class="panel" role="region" aria-labelledby="trigger-3" aria-hidden="true"><div class="panel-content"><p>Primero comprueba si hay encharcamiento, aire demasiado seco o exposición repentina al sol. Corta las hojas muy dañadas y mantén una buena ventilación; por lo general, en una o dos semanas verás que las hojas nuevas recuperan la salud.</p></div></div>
      </div>
      <div class="item">
        <button id="trigger-4" class="trigger" type="button" aria-expanded="false" aria-controls="panel-4"><span class="index">04</span><span>¿Cuándo es necesario cambiar la maceta?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-4" class="panel" role="region" aria-labelledby="trigger-4" aria-hidden="true"><div class="panel-content"><p>Si las raíces salen por los orificios de drenaje, el agua se escurre enseguida o el crecimiento se detiene claramente, en primavera puedes trasplantarla a una maceta nueva de dos a cuatro centímetros más de diámetro.</p></div></div>
      </div>
    </section>
  </main>
  <script>
    const triggers = [...document.querySelectorAll('.trigger')];
    triggers.forEach((trigger, index) => {
      trigger.addEventListener('click', () => {
        const willOpen = trigger.getAttribute('aria-expanded') !== 'true';
        triggers.forEach(item => {
          item.setAttribute('aria-expanded', 'false');
          const panel = document.getElementById(item.getAttribute('aria-controls'));
          panel.classList.remove('is-open');
          panel.setAttribute('aria-hidden', 'true');
        });
        if (willOpen) {
          trigger.setAttribute('aria-expanded', 'true');
          const panel = document.getElementById(trigger.getAttribute('aria-controls'));
          panel.classList.add('is-open');
          panel.setAttribute('aria-hidden', 'false');
        }
      });
      trigger.addEventListener('keydown', event => {
        const direction = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : 0;
        if (direction) {
          event.preventDefault();
          triggers[(index + direction + triggers.length) % triggers.length].focus();
        }
      });
    });
  </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