Carregando
Spinner para a ação que a pessoa disparou e espera, como o botão de salvar; esqueleto para o conteúdo que ainda vai chegar e já tem forma conhecida, como a linha da tabela. O esqueleto evita o salto: o espaço já está reservado com o tamanho do que vem. Só classe, sem JavaScript.
Spinner
Clique em salvar: o botão fica ocupado por dois segundos.
Esqueleto
A forma do que vem, no lugar do que vem.
Exemplos
O spinner mede em em e pinta com currentColor, então acompanha o lugar onde está sem variante nenhuma.
Botão ocupado
aria-busy="true" com disabled. No botão, o spinner ocupa o lugar e o tamanho do ícone.
No texto
Na altura da letra, e na cor dela.
Buscando cobranças…
Linha de tabela
O esqueleto pode ser qualquer elemento, inclusive um <td>.
| Cliente | Valor |
|---|---|
Avatar e card
is-circle fica redondo na largura que você der.
Como usar
<button class="tuc-btn is-primary" aria-busy="true" disabled>
<span class="tuc-spinner"></span> Salvando
</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>O esqueleto é um bloco de 1em de altura e da largura toda: a largura e, quando precisar, a altura são suas.
Não escreva spinner à mão, com SVG ou borda própria — o toast de carregando e os componentes usam este.
Ocupado não é desativado
O disabled impede o segundo clique, mas o esmaecido de 50% diz "não disponível", e o botão está trabalhando. Com aria-busy="true" ele fica com a cor cheia e o cursor de espera.
Com o HTMX
htmx-indicator é a classe do próprio HTMX: escondida, e visível só durante a requisição. Junto do
spinner, ele aparece e some sem uma linha 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> Salvar
</button>O esqueleto serve de conteúdo provisório para o que o HTMX vai buscar logo que a página abre. Quando a resposta chega, ela substitui o esqueleto no mesmo espaço, e nada pula.
<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>Acessibilidade
O texto diz o que está acontecendo, e o spinner confirma. "Salvando" ao lado do spinner é anunciado; um spinner sozinho é um círculo girando para quem não vê.
O esqueleto é decoração: não seleciona texto, não recebe ponteiro e seu texto, se houver, fica transparente. Com
prefers-reduced-motion o brilho do esqueleto para de vez, porque é só enfeite. O spinner continua girando,
mais devagar: parado, ele deixaria de dizer "esperando" e viraria um círculo qualquer.
Classes
| Classe ou atributo | Para quê |
|---|---|
.tuc-spinner | Círculo girando, de 1em, na cor do texto |
is-lg | No spinner: 1.5em |
.tuc-btn > .tuc-spinner | Dentro do botão, no tamanho do ícone dele |
aria-busy="true" | No .tuc-btn: ocupado, com cor cheia e cursor de espera, mesmo com disabled |
.tuc-skeleton | Bloco com brilho, no lugar do conteúdo que vem |
is-circle | No esqueleto: redondo, com a altura igual à largura |
htmx-indicator | Do HTMX: mostra o elemento só durante a requisição |