Acordeones sin JavaScript: <details> y <summary>

Domina el elemento details y summary de HTML para crear acordeones, FAQs y secciones colapsables de forma nativa, accesible y sin una línea de JavaScript.

¿Cuántas veces has instalado una librería de acordeones o escrito decenas de líneas de JavaScript para mostrar y ocultar contenido? Con <details> y <summary> el navegador lo hace por ti, de forma accesible y sin dependencias.


Cómo funciona <details>

<details> es un elemento desplegable. Su contenido está oculto por defecto y se muestra al hacer clic sobre el <summary>, que actúa como título o etiqueta del bloque.

<details>
  <summary>¿Qué es HTML?</summary>
  <p>
    HTML (HyperText Markup Language) es el lenguaje de marcado estándar 
    para crear páginas web. Define la estructura y el significado del contenido.
  </p>
</details>

Así de simple. El navegador añade el triángulo de apertura, gestiona la animación y expone el estado a las tecnologías de asistencia.

El atributo open

Por defecto <details> está cerrado. Si quieres que empiece abierto, añade el atributo open:

<details open>
  <summary>Esta sección está abierta por defecto</summary>
  <p>El contenido es visible al cargar la página.</p>
</details>

Detectar cambios de estado con JavaScript

El elemento emite un evento toggle cada vez que cambia entre abierto y cerrado:

<details id="acordeon">
  <summary>Más información</summary>
  <p>Contenido adicional.</p>
</details>

<script>
  const acordeon = document.getElementById('acordeon');
  acordeon.addEventListener('toggle', () => {
    if (acordeon.open) {
      console.log('Abierto');
    } else {
      console.log('Cerrado');
    }
  });
</script>

Estilizar <details> y <summary>

El triángulo por defecto es el marcador nativo del navegador. Puedes reemplazarlo completamente:

/* Ocultar el marcador nativo */
details > summary {
  list-style: none;
  cursor: pointer;
}
details > summary::-webkit-details-marker {
  display: none;
}

/* Estilo personalizado */
details {
  border: 1px solid #333;
  border-radius: 8px;
  padding: 0;
  overflow: hidden;
}

summary {
  padding: 1rem 1.25rem;
  font-weight: 600;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #1a1a2e;
  color: #fff;
}

/* Icono con CSS puro */
summary::after {
  content: '+';
  font-size: 1.25rem;
  transition: transform 0.2s;
}

details[open] summary::after {
  transform: rotate(45deg);
}

details > *:not(summary) {
  padding: 1rem 1.25rem;
}

Acordeón de preguntas frecuentes (FAQ)

Un caso de uso muy habitual es el bloque de preguntas frecuentes, que también tiene valor SEO gracias al marcado estructurado:

<section>
  <h2>Preguntas frecuentes</h2>

  <details>
    <summary>¿Cuánto tiempo dura el curso?</summary>
    <p>El curso tiene una duración aproximada de 6 horas de contenido.</p>
  </details>

  <details>
    <summary>¿Necesito conocimientos previos?</summary>
    <p>Solo necesitas saber qué es HTML a nivel básico.</p>
  </details>

  <details>
    <summary>¿Hay certificado al terminar?</summary>
    <p>Sí, recibirás un certificado de finalización por correo electrónico.</p>
  </details>
</section>

Acordeón exclusivo: solo uno abierto a la vez

El atributo name en <details> permite crear grupos exclusivos: al abrir uno, los demás del mismo grupo se cierran automáticamente. Es la solución nativa al acordeón clásico sin JavaScript:

<details name="faq">
  <summary>¿Pregunta 1?</summary>
  <p>Respuesta a la pregunta 1.</p>
</details>

<details name="faq">
  <summary>¿Pregunta 2?</summary>
  <p>Respuesta a la pregunta 2.</p>
</details>

<details name="faq">
  <summary>¿Pregunta 3?</summary>
  <p>Respuesta a la pregunta 3.</p>
</details>

Todos los <details> con el mismo name forman un grupo. El navegador garantiza que solo uno esté abierto a la vez.

Nota: el atributo name para grupos de acordeón tiene soporte en Chrome 120+ y Firefox 130+. Para Safari, considera un pequeño polyfill si necesitas compatibilidad total.


Animación de apertura

Por defecto la apertura es instantánea. Con CSS moderno puedes añadir una transición suave:

details > *:not(summary) {
  overflow: hidden;
  transition: height 0.3s ease;
}

/* Técnica con interpolate-size (CSS moderno) */
@supports (interpolate-size: allow-keywords) {
  :root {
    interpolate-size: allow-keywords;
  }
  details > *:not(summary) {
    height: 0;
  }
  details[open] > *:not(summary) {
    height: auto;
  }
}

<details> como widget de navegación

<details> no es solo para FAQs. Sirve para cualquier contenido colapsable: filtros de búsqueda, opciones avanzadas de formularios, secciones de código, notas al margen…

<!-- Filtros de búsqueda colapsables -->
<details>
  <summary>Filtros avanzados</summary>
  <form>
    <label>
      Categoría:
      <select name="categoria">
        <option>Todas</option>
        <option>Frontend</option>
        <option>Backend</option>
      </select>
    </label>
    <label>
      <input type="checkbox" name="gratis" /> Solo gratuitos
    </label>
  </form>
</details>

¿Qué hemos aprendido?

  • <details> + <summary> crean acordeones nativos sin JavaScript.
  • El atributo open controla el estado inicial.
  • El atributo name agrupa <details> para que solo uno esté abierto a la vez.
  • El evento toggle permite reaccionar a los cambios de estado.
  • Se puede animar con CSS sin trucos complicados.

Siguiente paso

En la próxima lección veremos los atributos data-*: cómo almacenar y acceder a datos personalizados directamente en los elementos HTML.