Por dentro da reescrita: Astro no documento, Solid na camada interativa

Como uma única ilha executada no cliente adiciona telemetria e presença de cursores sem entregar ao Solid o controle do portfólio inteiro.

15 de jul. de 2026

No post anterior, resumi a arquitetura do frontend em uma frase: o Astro gera a maior parte do site como conteúdo estático, e o Solid hidrata apenas as ilhas interativas.

Essa frase esconde uma das fronteiras mais úteis da reescrita.

O conteúdo do portfólio já faz parte do documento gerado. O Solid entra depois, quando o navegador precisa de telemetria, presença de cursores e estado compartilhado entre essas partes da interface. A reescrita eliminou fronteiras no deploy, mas preservou as separações que continuam sendo úteis dentro da aplicação.

Do container à página

O post anterior terminou com dois artefatos dentro de uma única imagem de runtime: a saída do Astro em dist e o executável compilado do servidor. Este post começa no primeiro deles.

A página inicial é uma página do Astro. Durante o build, ela resolve o idioma, cria a lista de projetos e renderiza o título, a descrição, a navegação e os links. O layout compartilhado do Astro cuida dos metadados do documento, das URLs canônicas, das versões em outros idiomas, das fontes e das transições entre páginas.

A camada interativa entra por uma única linha em index.astro:

<BaseLayout title={title} description={description} lang={locale}>
  <HomeLiveOverlay client:only="solid-js" />

  <main>
    <!-- Conteúdo do portfólio -->
  </main>
</BaseLayout>

Essa posição foi intencional. O componente do Solid fica ao lado do conteúdo principal, em vez de envolvê-lo. O Astro não precisa do Solid para renderizar o portfólio, e o Solid não precisa controlar a página inteira para adicionar comportamento em tempo real.

Build do AstroHTML, metadados, rotas eprojetosPágina do portfólioNavegador iniciaCamada interativa doSolidPainéis de telemetriaPresença de cursores

Por que esta ilha usa client:only

A diretiva client:only do Astro não renderiza o HTML do componente durante o build e deixa essa renderização para o navegador. Ela não é apenas uma forma curta de dizer “torne isto interativo”, nem representa automaticamente uma otimização de desempenho.

Para esta camada, deixar de renderizar o componente durante o build é a escolha correta. Ele depende de informações do navegador, como as dimensões da janela e a posição do ponteiro. Os dados de telemetria e dos cursores também não têm valor durante o build. Um painel de estatísticas gerado nesse momento já chegaria desatualizado ao navegador.

Mais importante: o estado vazio é válido. Enquanto a ilha não inicia, não há painéis de telemetria flutuando pela tela nem cursores remotos. O título, os links dos projetos, a navegação e os metadados não precisam de um estado de carregamento porque nunca estiveram dentro da ilha.

Se o JavaScript não carregar, a página continua sendo um portfólio. Se o fluxo de estatísticas ou a conexão WebSocket cair, o documento não desaparece junto. Esse modo de falha faz parte do design.

Uma ilha em vez de duas

A telemetria e a presença de cursores parecem funcionalidades separadas, mas compartilham uma interação. Quando um painel de telemetria está ativo, a camada de cursores muda a forma de exibir os rótulos.

Essa coordenação fica em um pequeno componente chamado HomeLiveOverlay:

export function HomeLiveOverlay() {
  const { selfId, cursors } = useCursorPresence();
  const [isStatsHovered, setIsStatsHovered] = createSignal(false);

  return (
    <>
      <TelemetryBackdrop
        placement="hero"
        onStatsHoverChange={setIsStatsHovered}
      />
      <CursorPresenceLayer
        selfId={selfId()}
        cursors={cursors()}
        isStatsHovered={isStatsHovered()}
      />
    </>
  );
}

Um sinal reativo do Solid é suficiente para conectá-las. Manter as duas camadas dentro da mesma ilha faz com que essa relação continue local. Separá-las em ilhas independentes exigiria outro mecanismo de comunicação para um estado que já pertence à mesma camada visual.

Esta é uma unidade útil para uma ilha neste projeto: não uma ilha por componente, nem uma única árvore de componentes para a página inteira, mas uma fronteira em torno dos comportamentos que precisam reagir em conjunto.

Estático não significa ausência de JavaScript

A fronteira não é “o Astro não tem JavaScript e o Solid tem todo ele”. O layout base ainda usa o roteador no cliente do Astro, e pequenos comportamentos da página podem continuar em scripts comuns. A diferença está na responsabilidade de cada parte.

O Astro cuida do documento e do conteúdo que precisa existir antes que o estado do navegador esteja disponível. O Solid cuida da árvore de componentes cujo resultado muda conforme sinais, fluxos de dados, posições do ponteiro e conexões são atualizados.

Essa escolha ainda tem um custo. client:only carrega a camada interativa imediatamente, e os painéis de telemetria concentram mais código executado no cliente do que o restante da página inicial. Aceito esse custo porque essa camada é a principal superfície dinâmica da página inicial. Se ela crescer muito além desse papel, o próximo passo será dividir ou adiar partes da camada interativa, não mover o conteúdo do portfólio para dentro dela.

A próxima fronteira

Agora o deploy tem um container e um processo de servidor. Dentro do navegador, a página continua dividida com clareza: o Astro fornece o documento, e o Solid adiciona as partes que só fazem sentido enquanto a página está em execução.

O próximo post acompanhará uma dessas partes ao atravessar a fronteira: o movimento do ponteiro no navegador, uma mensagem WebSocket tipada, uma identidade baseada em cookie e a camada do Solid que renderiza o cursor de outro visitante.