Cargando
Spinner para la acción que la persona disparó y está esperando, como el botón de guardar; esqueleto para el contenido que todavía va a llegar y ya tiene una forma conocida, como la fila de la tabla. El esqueleto evita el salto: el espacio ya está reservado con el tamaño de lo que viene. Solo clases, sin JavaScript.
Spinner
Haz clic en guardar: el botón queda ocupado durante dos segundos.
Esqueleto
La forma de lo que viene, en el lugar de lo que viene.
Ejemplos
El spinner se mide en em y se pinta con currentColor, así que acompaña el lugar donde está sin ninguna variante.
Botón ocupado
aria-busy="true" con disabled. En el botón, el spinner ocupa el lugar y el tamaño del icono.
En el texto
A la altura de la letra, y en su color.
Buscando cobros…
Fila de tabla
El esqueleto puede ser cualquier elemento, incluso un <td>.
| Cliente | Importe |
|---|---|
Avatar y tarjeta
is-circle queda redondo con el ancho que le des.
Cómo usar
<button class="tuc-btn is-primary" aria-busy="true" disabled>
<span class="tuc-spinner"></span> Guardando
</button>
<span class="tuc-spinner is-lg"></span>
<div class="tuc-skeleton" style="width:60%"></div>
<span class="tuc-skeleton is-circle" style="width:36px"></span>El esqueleto es un bloque de 1em de alto y de todo el ancho: el ancho y, cuando haga falta, el alto los decides tú.
No escribas un spinner a mano, con SVG o borde propio — el toast de carga y los componentes usan este.
Ocupado no es desactivado
disabled impide el segundo clic, pero el atenuado del 50% dice "no disponible", y el botón está trabajando. Con aria-busy="true" conserva el color pleno y el cursor de espera.
Con HTMX
htmx-indicator es la clase del propio HTMX: oculta, y visible solo durante la petición. Junto al
spinner, aparece y desaparece sin una línea de JavaScript.
<button class="tuc-btn is-primary" hx-post="{% url 'contract-save' contract.pk %}" hx-disabled-elt="this">
<span class="tuc-spinner htmx-indicator"></span> Guardar
</button>El esqueleto sirve de contenido provisional para lo que HTMX va a buscar en cuanto se abre la página. Cuando llega la respuesta, reemplaza al esqueleto en el mismo espacio, y nada salta.
<div hx-get="{% url 'customers-table' %}" hx-trigger="load" hx-swap="outerHTML">
<span class="tuc-skeleton" style="width:60%"></span>
<span class="tuc-skeleton" style="width:80%; margin-top:8px"></span>
</div>Accesibilidad
El texto dice lo que está pasando, y el spinner lo confirma. "Guardando" junto al spinner se anuncia; un spinner solo es un círculo girando para quien no ve.
El esqueleto es decoración: no selecciona texto, no recibe el puntero y su texto, si lo hay, queda transparente. Con
prefers-reduced-motion el brillo del esqueleto se detiene del todo, porque es solo adorno. El spinner sigue
girando, más despacio: quieto, dejaría de decir "esperando" y sería un círculo cualquiera.
Clases
| Clase o atributo | Para qué |
|---|---|
.tuc-spinner | Círculo girando, de 1em, en el color del texto |
is-lg | En el spinner: 1.5em |
.tuc-btn > .tuc-spinner | Dentro del botón, del tamaño de su icono |
aria-busy="true" | En .tuc-btn: ocupado, con color pleno y cursor de espera, incluso con disabled |
.tuc-skeleton | Bloque con brillo, en el lugar del contenido que viene |
is-circle | En el esqueleto: redondo, con el alto igual al ancho |
htmx-indicator | De HTMX: muestra el elemento solo durante la petición |