Skeleton CSS
O "esqueleto" cinza que brilha enquanto o conteúdo carrega — linhas de texto, título, foto, avatar, botão e selo, prontos para montar.
O que é
Sabe quando você abre um aplicativo e, antes do conteúdo aparecer, surgem blocos cinzas no formato do que vai vir (foto, nome, texto)? Isso é o skeleton. Ele avisa ao visitante "está carregando, já vem" e evita a página pular quando o conteúdo chega. Cada <div sw-skl="…"> é uma peça (uma linha de texto, uma foto, um avatar). Junte várias para desenhar a tela inteira carregando.
É só CSS — não precisa de JavaScript. O brilho que passa pela peça é uma animação que se desliga sozinha para quem pediu "reduzir movimento" no aparelho. Quando os dados chegarem, você troca o esqueleto pelo conteúdo real (apagando as peças ou escondendo o bloco) — o skeleton não faz isso sozinho.
Quando usar
- Lista ou grade que vem do servidor (produtos, posts, clientes): mostre o esqueleto no lugar dos cards até os dados chegarem.
- Perfil e comentários: avatar redondo (
avt) + duas linhas de texto (txt). - Foto grande que demora: retângulo de imagem (
img) no tamanho exato dela, para a página não pular. - Painel admin: números e tabelas carregando por AJAX.
- Para uma espera curta e sem formato (clique num botão), use o carregando do próprio botão (
sw-btn="pri loading"), não o skeleton.
Comece aqui
-
1
Coloque o SW Core no
<head>da página (uma vez só). O skeleton só precisa do CSS principal.<link rel="stylesheet" href="swcore/swcore.min.css"> -
2
Escreva uma peça por linha. Aqui: um título e duas linhas de texto. A última linha encolhe sozinha para 70%, como um parágrafo de verdade.
<div sw-skl="ttl"></div> <div sw-skl="txt"></div> <div sw-skl="txt"></div> -
3
Quando os dados chegarem, apague as peças e coloque o conteúdo real no lugar (veja a receita "Trocar pelo conteúdo" mais abaixo).
Todas as variações
Cada quadro abaixo é o componente de verdade, rodando. O código embaixo é exatamente o que está no quadro: copie e cole.
1. Texto e título
txt é uma linha de texto (ocupa a largura toda); ttl é um título (mais alto, 60% da largura). Com sm os dois ficam menores. Numa sequência, a última linha txt encolhe para 70%.
<div sw-skl="txt"></div>
<div sw-skl="txt"></div>
<div sw-skl="txt"></div>
<div sw-skl="txt sm"></div>
<div sw-skl="txt sm"></div>
<div sw-skl="ttl"></div>
<div sw-skl="ttl sm"></div>
2. Avatar (foto redonda de pessoa)
Mesma escala de tamanhos do avatar de verdade do SW Core. Sem tamanho, fica no médio (40 px).
<div sw-skl="avt pp"></div>
<div sw-skl="avt p"></div>
<div sw-skl="avt"></div>
<div sw-skl="avt g"></div>
3. Imagem
Ocupa a largura toda do espaço e calcula a altura pela proporção. Use a mesma proporção da foto que vai entrar, para a página não pular.
<div sw-skl="img"></div>
<div sw-skl="img sqr"></div>
<div sw-skl="img wd"></div>
4. Botão e selo
Com as mesmas medidas do botão real (sw-btn) e de um selo curto.
<div sw-skl="btn pp"></div>
<div sw-skl="btn"></div>
<div sw-skl="btn g"></div>
<div sw-skl="badge"></div>
5. Formas livres
crc (círculo) e sqr (quadrado de canto discreto) não têm tamanho próprio: pegam o tamanho do lugar onde estão. Aqui, numa grade de 3 colunas com class="ar-sq" (altura igual à largura).
<div class="l l-3">
<div sw-skl="crc" class="ar-sq"></div>
<div sw-skl="crc" class="ar-sq"></div>
<div sw-skl="crc" class="ar-sq"></div>
</div>
<div class="l l-3">
<div sw-skl="sqr" class="ar-sq"></div>
<div sw-skl="sqr" class="ar-sq"></div>
<div sw-skl="sqr" class="ar-sq"></div>
</div>
6. Montando uma tela
O skeleton de verdade é a soma das peças no formato do conteúdo que vai chegar.
<div sw-card class="p-6">
<div class="flx alc gap-3 mb-4"><div sw-skl="avt"></div><div class="f-1"><div sw-skl="txt sm"></div><div sw-skl="txt sm"></div></div></div>
<div sw-skl="img" class="mb-4"></div>
<div sw-skl="ttl"></div>
<div sw-skl="txt"></div>
<div sw-skl="txt"></div>
<div class="flx gap-3 mt-4"><div sw-skl="btn pp"></div><div sw-skl="btn pp"></div></div>
</div>
<div class="flx f-cl gap-4">
<div class="flx gap-3"><div sw-skl="avt p"></div><div class="f-1"><div sw-skl="ttl sm"></div><div sw-skl="txt sm"></div><div sw-skl="txt sm"></div></div></div>
<div class="flx gap-3"><div sw-skl="avt p"></div><div class="f-1"><div sw-skl="ttl sm"></div><div sw-skl="txt sm"></div><div sw-skl="txt sm"></div></div></div>
</div>
Tabela de opções
| Palavra | O que faz | Exemplo |
|---|---|---|
| Texto | ||
txt | Linha de texto: 14 px de altura, largura toda. A última de uma sequência fica com 70%. | sw-skl="txt" |
ttl | Título: 22 px de altura, 60% da largura. | sw-skl="ttl" |
sm | Junto de txt ou ttl: versão menor. | sw-skl="txt sm" |
| Avatar | ||
avt | Círculo de 40 px (foto de pessoa). | sw-skl="avt" |
avt pp | 24 px. | sw-skl="avt pp" |
avt p | 32 px. | sw-skl="avt p" |
avt g | 56 px. | sw-skl="avt g" |
| Imagem | ||
img | Largura toda, proporção 16:9. | sw-skl="img" |
img sqr | Proporção 1:1 (quadrada). | sw-skl="img sqr" |
img wd | Proporção 21:9 (faixa larga). | sw-skl="img wd" |
| Botão e selo | ||
btn | Botão de 120 × 44 px. | sw-skl="btn" |
btn pp | Botão de 80 × 28 px. | sw-skl="btn pp" |
btn g | Botão de 160 × 56 px. | sw-skl="btn g" |
badge | Selo em cápsula de 64 × 20 px. | sw-skl="badge" |
| Formas livres (você dá o tamanho) | ||
crc | Círculo. Largura e altura vêm do lugar onde está ou do seu CSS. | sw-skl="crc" class="ar-sq" |
sqr | Quadrado de canto discreto. Mesmo caso do crc. | sw-skl="sqr" class="ar-sq" |
Receitas prontas — usos reais
Situações que aparecem em quase todo site. Copie a que servir.
Cliente desde 2021
<div id="perfil" aria-busy="true">
<!-- esqueleto: aparece primeiro -->
<div data-skl>
<div class="flx alc gap-3">
<div sw-skl="avt g"></div>
<div class="f-1"><div sw-skl="ttl sm"></div><div sw-skl="txt sm"></div></div>
</div>
</div>
<!-- conteúdo real: escondido até chegar -->
<div data-real hidden></div>
</div>
<script>
fetch('/api/perfil').then((r) => r.json()).then((p) => {
const bloco = document.getElementById('perfil');
const real = bloco.querySelector('[data-real]');
real.textContent = p.nome; // monte o conteúdo aqui
bloco.querySelector('[data-skl]').remove(); // tira o esqueleto
real.hidden = false;
bloco.removeAttribute('aria-busy');
});
</script>
<div class="l l-3">
<div sw-card class="p-4">
<div sw-skl="img sqr" class="mb-4"></div> <!-- foto do produto -->
<div sw-skl="ttl sm"></div> <!-- nome -->
<div sw-skl="txt sm"></div> <!-- preço -->
<div sw-skl="btn pp" class="mt-3"></div> <!-- botão comprar -->
</div>
<!-- repita o card quantas vezes quiser -->
</div>
<div class="flx alc gap-4">
<div sw-skl="avt p"></div> <!-- foto do cliente -->
<div class="f-1"><div sw-skl="txt sm"></div></div> <!-- nome -->
<div sw-skl="badge"></div> <!-- status -->
</div>
Precisa de quê
swcore.min.css— o skeleton.
Cuidados — erros comuns
- O skeleton não some sozinho. Quando os dados chegarem, é o seu código que apaga as peças (ou esconde o bloco) e mostra o conteúdo.
- Uma linha
txtsozinha já fica com 70% (ela é "a última"). Para a largura toda, coloque outra peça depois dela ou use duas linhas. crcesqrsem tamanho ficam invisíveis (0 px). Dê largura e altura pelo lugar onde estão ou pelo CSS da página.- Desenhe o esqueleto no mesmo formato do conteúdo real (mesma proporção de foto, mesmo número de linhas). Se for muito diferente, a página pula quando o conteúdo chega — e o skeleton perde a razão de existir.
- Para esconder/mostrar com o atributo
hidden, coloque-o numa<div>simples por fora. Numa caixa comclass="flx"ohiddennão esconde (a classe manda mostrar como bloco lado a lado). - Coloque
aria-busy="true"no bloco que está carregando e tire quando terminar: assim o leitor de tela sabe que o conteúdo ainda vem.
O que ele não faz
- Não tem cores nem velocidades diferentes: o brilho é sempre o mesmo cinza que acompanha o tema claro/escuro.
- Não busca dados nem controla a troca. É só a aparência do "carregando".

