Tucano v0.37.2

Acordeón

Bloques que se pliegan y se despliegan, hechos sobre <details> y <summary>. Lo nativo ya resuelve teclado, semántica y estado, y abre y cierra antes de que cargue el JavaScript. El componente entra solo donde lo nativo no llega: animar.

¿Necesito un build en mi proyecto?

No. El CSS ya viene compilado y Tailwind se usa solo dentro de la biblioteca. De tu lado son dos archivos y nada más.

¿Funciona con HTMX?

Sí. Lo que llega por swap se inicializa solo, así que el acordeón que viene del servidor ya nace animado.

¿Y si falla el JavaScript?

Este acordeón sigue abriendo y cerrando: es un <details> nativo. Lo único que se pierde es la animación.

Ejemplos

El mismo <details>, con un atributo o una clase de más.

Varios abiertos

Sin data-single, cada elemento abre y cierra por su cuenta.

Datos del contrato

Número 2026-0142, vigencia de 12 meses.

Partes

Tucano Comercial S.L. y Panadería Pan Caliente.

Cláusulas

Ajuste anual por inflación y recargo del 2% por retraso.

Uno a la vez

data-single="true" pliega los demás al abrir uno.

Plan mensual

Cobro cada día 10, cancela cuando quieras.

Plan anual

Dos meses de descuento, pagado de una vez.

A consultar

Para más de 50 usuarios.

Contenido de verdad dentro

La altura la mide el navegador, no se adivina: formulario, tabla e imagen abren igual.

Filtros del informe
Columnas visibles

Sin divisores, para menú

is-plain quita las líneas y deja el título con aspecto de etiqueta de grupo.

Cómo usar

El HTML es el de siempre. Los <details> deben ser hijos directos del elemento marcado, y el open escrito en la plantilla indica cuáles nacen abiertos.

<div class="tuc-accordion" data-tuc-accordion data-single="true">
  <details open>
    <summary>Proyectos</summary>
    <p>Listar, crear y seguir inspecciones.</p>
  </details>
  <details>
    <summary>Informes</summary>
    <p>Mensual, por equipo y por período.</p>
  </details>
</div>

El script añade lo que falta: la clase tuc-accordion en el contenedor, tuc-accordion__item en cada <details>, tuc-accordion__trigger en el <summary>, la flecha, y envuelve el resto del contenido en tuc-accordion__body y tuc-accordion__content. Hasta entonces, el CSS ya dibuja el acordeón en crudo con las mismas líneas y el mismo espaciado, para que nada salte cuando llega el JavaScript.

En la plantilla de Django

<div class="tuc-accordion" data-tuc-accordion data-single="true">
  {% for question in questions %}
    <details{% if forloop.first %} open{% endif %}>
      <summary>{{ question.title }}</summary>
      {{ question.answer|linebreaks }}
    </details>
  {% endfor %}
</div>

En JavaScript

const faq = new Tucano.Accordion('#faq', { single: true });

const item = document.querySelector('#faq > details:nth-child(2)');
faq.open(item);      // con single, pliega los demás
faq.close(item);     // anima y solo entonces quita el open
faq.items;           // los <details>, en orden
faq.destroy();

Cómo anima

La altura del contenido es auto, y auto no tiene transición. Al cerrar, además, el navegador oculta el contenido en el mismo fotograma en que cae el open.

La apertura es CSS puro: el cuerpo es un grid de una fila, y la fila anima de 0fr a 1fr — interpolable, sin medir nada en JavaScript ni fijar altura. El cierre necesita script: el clic en el <summary> se intercepta, el elemento recibe is-closing y sigue abierto mientras la fila vuelve a 0fr, y solo al final cae el open. Reabrir en mitad del cierre cancela la salida y sigue desde el punto en que estaba la altura.

El fin de la animación viene del transitionend, no de un número

Un tiempo en JavaScript tendría que reflejar el token del CSS, y los dos se desfasan: 220 ms contra 280 ms arrancaba el contenido antes del final, y el cierre se veía cortado. El elemento se cierra cuando la fila del grid termina su transición; un timeout de 500 ms queda solo como red de seguridad, para pestaña oculta o movimiento reducido, cuando el evento no llega.

El espacio inferior es margen, no padding

Una pista en fr no se encoge por debajo del mínimo del contenido, y el padding cuenta en ese mínimo: con padding en el contenido, el elemento cerrado dejaba una rendija de 14px. Por eso el espacio sale del margen del último hijo, que el overflow: hidden recorta hasta cero. Si estilizas el contenido, mantén la regla.

La primera apertura de un <details> suele atascarse, porque el navegador todavía no midió lo que estaba cerrado. Al iniciar, el componente abre y cierra cada elemento en el mismo bloque síncrono — no llega a pintarse nada — solo para adelantar ese cálculo. Con prefers-reduced-motion, la transición baja a 1 ms.

Teclado y accesibilidad

Todo aquí viene del <details>: el <summary> es enfocable, y el lector de pantalla anuncia plegado y desplegado sin ningún aria-* nuestro.

TeclaAcción
TabVa de título en título, y entra en el contenido de los elementos abiertos
Enter EspacioAbre o cierra el elemento enfocado, con la misma animación del clic

La flecha es dibujo y lleva aria-hidden, para no leerse junto con el título. Un <details> cerrado no tiene nada enfocable en el recorrido del Tab: el campo de un elemento plegado no se alcanza hasta que alguien lo abre.

API

Generada a partir del código en cada build — si algo no está aquí, no existe.

Marcado
[data-tuc-accordion]
En JS
new Tucano.Accordion(alvo, opcoes)
Atributos
data-single
Métodos
open close destroy

Opciones

Las notas de esta tabla salen de los comentarios del código fuente, que están escritos en portugués.

OpciónPor defectoPara qué
singlefalseabrir um recolhe os outros