SW Core
SW Core / CSS / Skeleton

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. 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. 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. 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%.

txt — linhas de texto <div sw-skl="txt"></div> <div sw-skl="txt"></div> <div sw-skl="txt"></div>
txt sm — linhas pequenas <div sw-skl="txt sm"></div> <div sw-skl="txt sm"></div>
ttl — título <div sw-skl="ttl"></div>
ttl sm — título pequeno <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).

avt pp — 24 px <div sw-skl="avt pp"></div>
avt p — 32 px <div sw-skl="avt p"></div>
avt — 40 px (padrão) <div sw-skl="avt"></div>
avt g — 56 px <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.

img — 16:9 (padrão, foto deitada) <div sw-skl="img"></div>
img sqr — 1:1 (quadrada, produto) <div sw-skl="img sqr"></div>
img wd — 21:9 (faixa larga, banner) <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.

btn pp — botão bem pequeno <div sw-skl="btn pp"></div>
btn — botão médio (padrão) <div sw-skl="btn"></div>
btn g — botão grande <div sw-skl="btn g"></div>
badge — selo / etiqueta <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).

crc — círculo <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>
sqr — quadrado <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.

Card de post (avatar, foto, título, texto, botões)
<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>
Lista de comentários
<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

PalavraO que fazExemplo
Texto
txtLinha de texto: 14 px de altura, largura toda. A última de uma sequência fica com 70%.sw-skl="txt"
ttlTítulo: 22 px de altura, 60% da largura.sw-skl="ttl"
smJunto de txt ou ttl: versão menor.sw-skl="txt sm"
Avatar
avtCírculo de 40 px (foto de pessoa).sw-skl="avt"
avt pp24 px.sw-skl="avt pp"
avt p32 px.sw-skl="avt p"
avt g56 px.sw-skl="avt g"
Imagem
imgLargura toda, proporção 16:9.sw-skl="img"
img sqrProporção 1:1 (quadrada).sw-skl="img sqr"
img wdProporção 21:9 (faixa larga).sw-skl="img wd"
Botão e selo
btnBotão de 120 × 44 px.sw-skl="btn"
btn ppBotão de 80 × 28 px.sw-skl="btn pp"
btn gBotão de 160 × 56 px.sw-skl="btn g"
badgeSelo em cápsula de 64 × 20 px.sw-skl="badge"
Formas livres (você dá o tamanho)
crcCírculo. Largura e altura vêm do lugar onde está ou do seu CSS.sw-skl="crc" class="ar-sq"
sqrQuadrado 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.

Trocar pelo conteúdo quando os dados chegarem Ao vivo
<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>
Grade de produtos carregando (loja) Ao vivo
<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>
Linha de tabela do painel carregando Ao vivo
<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 txt sozinha já fica com 70% (ela é "a última"). Para a largura toda, coloque outra peça depois dela ou use duas linhas.
  • crc e sqr sem 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 com class="flx" o hidden nã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".