Acordeón HTML nativo sin librerías: 2 métodos (2026)

Quieres un acordeón HTML listo para pegar, sin jQuery y sin librerías. Aquí lo tienes. Te enseño dos formas de montar un menú acordeón que funciona hoy en cualquier navegador moderno: una con las etiquetas nativas <details> y <summary>, y otra con CSS puro para controlar el diseño al detalle. Copias, pegas y funciona.

Este post usaba jQuery. Ya no hace falta. El navegador trae el comportamiento de plegar y desplegar de serie, así que te ahorras kilobytes, dependencias y problemas de accesibilidad. Vamos al grano.

Método 1: acordeón HTML con details y summary

Es la opción que te aconsejo empezar a usar. El elemento <details> crea un panel plegable y <summary> es la cabecera en la que se hace clic. El navegador se encarga del resto: abre, cierra, y es accesible por teclado sin que escribas ni una línea de JavaScript.

<details class="acordeon-item">
  <summary>¿Qué es un acordeón HTML?</summary>
  <p>Un componente que muestra u oculta contenido al hacer clic
     en su cabecera. Ideal para FAQs y menús.</p>
</details>

<details class="acordeon-item">
  <summary>¿Necesito jQuery?</summary>
  <p>No. Las etiquetas details y summary lo resuelven solas.</p>
</details>

Con esto ya tienes un acordeón funcional. Ahora le damos estilo. Este CSS quita la flecha por defecto, añade la tuya y deja todo limpio.

.acordeon-item {
  border: 1px solid #e2e2e2;
  border-radius: 8px;
  margin-bottom: .5rem;
  overflow: hidden;
}

.acordeon-item summary {
  cursor: pointer;
  padding: 1rem;
  font-weight: 600;
  list-style: none;              /* oculta el triángulo nativo */
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.acordeon-item summary::-webkit-details-marker { display: none; }

.acordeon-item summary::after {
  content: "+";
  font-size: 1.4rem;
  line-height: 1;
}

.acordeon-item[open] summary::after { content: "–"; }

.acordeon-item p { padding: 0 1rem 1rem; margin: 0; }

Ventajas: es semántico, funciona sin JS, se puede buscar con Ctrl+F aunque esté cerrado y es accesible por defecto. Si buscabas un menu acordeon rápido y honesto, este es el camino.

Que solo uno quede abierto: el atributo name

Un acordeón de verdad cierra un panel cuando abres otro. Antes esto pedía JavaScript. Hoy no. Basta con dar el mismo atributo name a todos los <details> del grupo. El navegador los trata como excluyentes, igual que un grupo de radios.

<details class="acordeon-item" name="faq">
  <summary>Primera pregunta</summary>
  <p>Al abrir otra, esta se cierra sola.</p>
</details>

<details class="acordeon-item" name="faq">
  <summary>Segunda pregunta</summary>
  <p>Mismo name, así que solo una queda abierta.</p>
</details>

El atributo name en <details> es compatible con los navegadores modernos (Chrome, Edge, Firefox y Safari recientes). Si un navegador antiguo no lo entiende, no rompe nada: simplemente permitirá tener varios paneles abiertos a la vez. Es una mejora progresiva perfecta. Puedes confirmar el soporte y los detalles en la documentación de MDN sobre details.

Método 2: menú acordeón en CSS puro con el truco del radio

¿Y si quieres control total del diseño y una animación de altura suave? Aquí entra el menu acordeon css clásico: inputs de tipo radio ocultos más labels. Usar radio (y no checkbox) garantiza que solo un panel quede abierto, porque comparten el mismo name.

<div class="acc">
  <div class="acc__item">
    <input type="radio" name="acc" id="acc-1" checked>
    <label for="acc-1">Panel 1</label>
    <div class="acc__panel">
      <p>Contenido del primer panel.</p>
    </div>
  </div>

  <div class="acc__item">
    <input type="radio" name="acc" id="acc-2">
    <label for="acc-2">Panel 2</label>
    <div class="acc__panel">
      <p>Contenido del segundo panel.</p>
    </div>
  </div>
</div>

El CSS oculta el radio, convierte el label en cabecera y anima la altura del panel con grid-template-rows, la técnica más fiable hoy para pasar de 0 a alto automático.

.acc__item { border: 1px solid #e2e2e2; border-radius: 8px; margin-bottom: .5rem; }
.acc input { position: absolute; opacity: 0; pointer-events: none; }

.acc label {
  display: block;
  padding: 1rem;
  font-weight: 600;
  cursor: pointer;
}

.acc__panel {
  display: grid;
  grid-template-rows: 0fr;                 /* cerrado */
  transition: grid-template-rows .3s ease;
}

.acc__panel > * { overflow: hidden; }

.acc input:checked ~ .acc__panel {
  grid-template-rows: 1fr;                 /* abierto */
}

.acc input:focus-visible + label {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

Este patrón te da una animación suave de apertura, algo que <details> no anima por sí solo en todos los navegadores. Ten en cuenta una limitación honesta: con radios siempre hay un panel abierto y no puedes cerrar el último con clic. Si necesitas poder cerrarlos todos, cambia a checkbox, aunque entonces podrán abrirse varios a la vez.

Este truco es primo del menu vertical desplegable css y de otros efectos que puedes montar sin scripts. Si te gusta esta línea, échale un vistazo a mis menús animados con CSS3 y a las microinteracciones web con ejemplos de código.

Cuándo necesitas un poco de JavaScript

Para el 90% de los casos no vas a necesitar JavaScript. Pero hay situaciones concretas en las que un pequeño script te viene bien:

  • Cargar el contenido del panel de forma diferida (lazy) al abrirlo.
  • Recordar qué panel dejó abierto el usuario usando localStorage.
  • Sincronizar el acordeón con la URL o con un ancla para enlaces profundos.
  • Animar la altura de <details> con control fino de easing.

Incluso en esos casos, el JS es mínimo y se apoya en el HTML nativo. Este ejemplo escucha el evento toggle de <details>, que el navegador dispara al abrir o cerrar.

document.querySelectorAll('details.acordeon-item').forEach((el) => {
  el.addEventListener('toggle', () => {
    if (el.open) console.log('Se abrió:', el.querySelector('summary').textContent);
  });
});

Nada de librerías de 80 KB para plegar un texto. El navegador ya hace el trabajo pesado.

Accesibilidad y prefers-reduced-motion

La gran ventaja de <details> es que ya es accesible: se navega con Tab, se abre con Enter o Espacio y los lectores de pantalla anuncian su estado. No tienes que añadir role ni aria-expanded a mano.

Si usas el método CSS con radios, cuida el foco visible (ya lo incluí con :focus-visible) y que los labels sean clicables con teclado. Y siempre, en cualquiera de los dos métodos, respeta a quien prefiere menos animación. Añade este bloque al final de tu CSS.

@media (prefers-reduced-motion: reduce) {
  .acc__panel { transition: none; }
}

Con esa media query, quien tenga activada la opción de reducir movimiento verá el panel abrirse al instante, sin transición. Es un detalle pequeño que marca la diferencia en accesibilidad real.

Preguntas frecuentes

¿Cómo hago un acordeón HTML sin jQuery?

Usa las etiquetas nativas <details> y <summary>. El navegador ya sabe plegar y desplegar el contenido, así que no necesitas jQuery ni ninguna librería. Copia el código del método 1 y funciona.

¿Cómo consigo que solo un panel quede abierto?

Con <details>, da el mismo atributo name a todos los paneles del grupo. Con el método CSS, usa inputs type="radio" que compartan el mismo name. En ambos casos, abrir uno cierra el anterior.

¿Se puede animar la apertura de un menú acordeón en CSS?

Sí. La forma más fiable es el truco de grid-template-rows de 0fr a 1fr con transición, como en el método 2. Anima de altura cero a altura automática sin saber el alto de antemano.

¿Es accesible un acordeón hecho con details y summary?

Sí, por defecto. Es navegable por teclado, se abre con Enter o Espacio y los lectores de pantalla anuncian si está abierto o cerrado. No necesitas añadir atributos ARIA manualmente.

¿Sirve este acordeón como menú vertical desplegable?

Sí. Apila varios <details> o varios .acc__item en columna y tienes un menú vertical desplegable con CSS. Dentro de cada panel puedes poner enlaces en lugar de texto.

Más snippets que te vendrán bien

Si te ha servido este acordeón, en la misma línea de código listo para copiar tienes cómo hacer un slider en HTML5 y CSS3 y cómo personalizar el botón de subir archivos. Todo sin librerías, como este.

Elige el método según lo que necesites: <details> si quieres lo más simple y accesible, o el CSS con radios si buscas animación y control total del diseño. Copia, pega y a tu proyecto. Si te ha sido útil, compártelo.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio