Back

Geração Automática de Skeleton Screens com boneyard

Geração Automática de Skeleton Screens com boneyard

Skeleton loaders são um daqueles padrões de UI que parecem simples até você realmente implementá-los. Você mede elementos do DOM, fixa alturas no código, configura renderização condicional — e então o seu designer altera o layout do card e você faz tudo de novo. O custo de manutenção se acumula silenciosamente.

O boneyard-js adota uma abordagem diferente: em vez de escrever a UI do skeleton manualmente, ele extrai dados de layout diretamente dos seus componentes renderizados em tempo de desenvolvimento e gera as definições dos placeholders automaticamente.

Principais Conclusões

  • O boneyard-js gera skeleton loaders automaticamente capturando dados de layout dos seus componentes reais, eliminando a necessidade de manter duas representações paralelas da mesma UI.
  • O escaneamento do DOM acontece em tempo de desenvolvimento através do Playwright, produzindo arquivos .bones.json estáticos. Nenhuma travessia de DOM em runtime ocorre em produção.
  • A captura é executada em três larguras de viewport por padrão (375, 768, 1280px), com valores horizontais armazenados como percentuais para comportamento responsivo.
  • A prop fixture e a flag --wait lidam com componentes que dependem de dados assíncronos indisponíveis durante a captura.
  • Um plugin para Vite mantém os bones sincronizados automaticamente a cada atualização HMR, removendo a necessidade de uma etapa CLI separada.
  • Existem adaptadores para React, Vue, Svelte 5, Angular, Preact e React Native, todos compartilhando o mesmo formato central.

O Problema com Skeleton Loaders Manuais

A maioria das implementações de skeleton loaders está desconectada da UI real. Você constrói o componente real e, separadamente, constrói um skeleton que aproxima sua forma. Quando o componente muda, o skeleton fica desatualizado. Com o tempo, seus estados de loading deixam de corresponder ao seu conteúdo, causando layout shifts e inconsistência visual.

Bibliotecas como react-loading-skeleton reduzem o boilerplate, mas ainda exigem que você descreva a estrutura manualmente. Você ainda está mantendo duas representações do mesmo componente.

Como o boneyard-js Gera Skeleton Screens Automaticamente

O boneyard-js inverte o fluxo de trabalho. Você envolve seu componente real em uma tag <Skeleton>, executa um comando CLI, e a ferramenta captura o layout para você.

Veja como isso fica em React:

import { Skeleton } from 'boneyard-js/react'

function ActivityPanel() {
  const { data, isLoading } = useFetch('/api/activity')

  return (
    <Skeleton name="activity" loading={isLoading}>
      {data && <ActivityContent data={data} />}
    </Skeleton>
  )
}

Então, com seu servidor de desenvolvimento rodando:

npx boneyard-js build

A CLI abre um navegador headless via Playwright, visita seu app, encontra todos os elementos <Skeleton name="..."> e percorre a árvore do DOM dentro deles. Ela usa getBoundingClientRect() em nós folha — elementos de texto, imagens, botões, inputs de formulário — e registra suas posições e tamanhos relativos à raiz do skeleton. Valores horizontais são armazenados como percentuais para comportamento responsivo, enquanto valores verticais são armazenados em pixels. O border radius é detectado automaticamente.

Esse processo é executado em três larguras de viewport por padrão (375, 768, 1280px), produzindo um arquivo .bones.json por componente:

{
  "breakpoints": {
    "375": {
      "bones": [
        { "x": 0, "y": 32, "w": 43.59, "h": 34, "r": 8 },
        { "x": 43.59, "y": 39, "w": 23.76, "h": 33, "r": 999 }
      ]
    }
  }
}

Um arquivo de registro (registry.js ou registry.ts) também é gerado. Importe-o uma vez no ponto de entrada do seu app e cada definição de skeleton se torna disponível globalmente:

import './bones/registry'

Em runtime, o componente <Skeleton> lê os dados de bones registrados para o seu name, renderiza o layout correspondente como retângulos posicionados absolutamente, e os substitui pelo conteúdo real quando loading se torna false.

Captura em Build-Time, Não Escaneamento em Runtime

Uma distinção importante: o escaneamento do DOM acontece durante o desenvolvimento, não em produção. Os arquivos .bones.json são artefatos estáticos versionados junto com seu código-fonte. Em runtime, o boneyard-js apenas lê essas definições pré-geradas. Não há travessia de DOM ao vivo no navegador.

Para React Native, o mecanismo de captura é diferente. O componente <Skeleton> escaneia a árvore de fibers em modo dev usando UIManager, mede views nativas e envia esses dados para a CLI. Em builds de produção, esse código de escaneamento é excluído por completo.

Lidando com Conteúdo Dinâmico e a Prop fixture

Se o seu componente depende de uma API que não está disponível durante a captura via CLI, o skeleton pode ser gerado incorretamente porque a área de conteúdo está vazia. Duas opções resolvem isso:

Use --wait para atrasar a captura após o carregamento da página:

npx boneyard-js build --wait 2000

Use a prop fixture para fornecer dados mock estáticos especificamente para a captura:

<Skeleton
  name="activity"
  loading={isLoading}
  fixture={<ActivityContent data={mockData} />}
>
  {data && <ActivityContent data={data} />}
</Skeleton>

O conteúdo de fixture é renderizado apenas durante a captura via CLI e não tem efeito em produção.

Atenção: Se o seu componente não renderizar nada enquanto data for undefined, o elemento wrapper colapsa para altura zero e o skeleton não será exibido. Defina um minHeight no <Skeleton> para evitar isso.

Plugin Vite para Integração Mais Estreita

Para projetos baseados em Vite, você pode pular completamente o terminal separado da CLI:

// vite.config.ts
import { defineConfig } from 'vite'
import { boneyardPlugin } from 'boneyard-js/vite'

export default defineConfig({
  plugins: [boneyardPlugin()]
})

Os bones são capturados quando o servidor de desenvolvimento inicia e recapturados automaticamente a cada atualização HMR. Isso mantém seus skeleton loaders gerados em build-time sincronizados com sua UI sem qualquer intervenção manual.

Suporte a Frameworks

O boneyard-js disponibiliza adaptadores específicos por framework como exports de pacote separados:

FrameworkImport
Reactboneyard-js/react
Vueboneyard-js/vue
Svelte 5boneyard-js/svelte
Angularboneyard-js/angular
Preactboneyard-js/preact
React Nativeboneyard-js/native

A lógica central de extração e o formato .bones.json são compartilhados entre todos eles.

Vale a Pena Adotar o boneyard-js?

O boneyard-js é uma ferramenta relativamente nova, então espere que a API evolua. Dito isso, o conceito central é sólido: gerar UI de skeleton a partir de dados reais de layout em vez de mantê-la manualmente.

O benefício prático é mais evidente em projetos onde os componentes mudam com frequência. Em vez de atualizar placeholders de skeleton após cada iteração de design, você executa novamente um único comando. Os arquivos .bones.json são atualizados, e os estados de loading do seu frontend permanecem precisos.

Se você já está gastando tempo mantendo skeleton loaders sincronizados com sua UI real, a automação compensa o custo de configuração.

Conclusão

Skeleton loaders não deveriam se desviar dos componentes que representam, mas implementações manuais quase sempre o fazem. O boneyard-js aborda isso tratando skeletons como um artefato gerado, em vez de algo escrito à mão. A etapa de captura é executada em desenvolvimento, a saída é um arquivo JSON estático, e o custo em runtime é mínimo. Para times iterando rapidamente em UI, esse fluxo de trabalho economiza tempo real e mantém os estados de loading visualmente fiéis aos componentes subjacentes.

FAQs

Nenhum escaneamento de DOM ocorre em produção. A CLI ou o plugin Vite geram arquivos .bones.json estáticos em tempo de desenvolvimento. Em produção, o componente Skeleton lê essas definições e renderiza retângulos posicionados absolutamente, adicionando apenas um pequeno custo em runtime.

Ele captura bones em três larguras de viewport por padrão: 375, 768 e 1280 pixels. Posições e larguras horizontais são armazenadas como percentuais para que o skeleton escale fluidamente entre breakpoints, enquanto valores verticais permanecem em pixels para espaçamento previsível. O runtime seleciona o breakpoint mais próximo com base na largura atual do viewport.

Você tem duas opções. A flag --wait atrasa a captura após o carregamento da página para dar tempo às requisições assíncronas de serem resolvidas. Alternativamente, a prop fixture aceita uma versão mock do seu componente preenchida com dados estáticos, que renderiza apenas durante a captura via CLI. Ambas as abordagens garantem que o skeleton reflita um layout populado em vez de um container vazio.

Sim, e você deveria. Os arquivos .bones.json e registry.js são artefatos estáticos que descrevem seus layouts de skeleton. Versioná-los mantém seu time alinhado, torna os builds reproduzíveis sem executar a etapa de captura no CI, e permite que o code review identifique mudanças inesperadas de layout junto com as edições de componente que as causaram.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay