Communitygithub.com

anhlamtruong/living-ui-swarm

Crie uma animação de "agrupamento de agentes" para construir a página no React / Next.js. Uma equipe de cursores coloridos por função (planejador, codificadores, designers, layout, testador, revisor, implantador) monta cada seção conforme ela aparece na rolagem.

O que é living-ui-swarm?

Esta skill implementa um mecanismo de animação de construção de página orientada por agentes. Ele utiliza um canvas overlay fixo que desenha uma pequena equipe de cursores que "constroem" cada seção da página conforme ela entra na viewport. O conteúdo é HTML real renderizado no servidor; o motor apenas oculta itens por alguns segundos e executa coreografias curtas para cada um. A skill lida com tipos de itens como molduras, digitação, revelação, imagens e duplicação, cada um com sua própria timeline do GSAP. Ela suporta Next.js (App Router) com Lenis para rolagem suave e GSAP para animações. Inclui um sistema de planos, construção e revisão com tarefas distribuídas por papéis de agentes, orçamentos de tempo por seção e um log ao vivo. A animação respeita preferências de movimento reduzido, pausa em abas ocultas, e é inteligente para evitar repetições em visitas subsequentes usando sessionStorage. A skill é ideal para páginas de portfólio, lançamento ou demonstração onde o ato de construção é a própria mensagem, mas não é recomendada para dashboards, documentação ou páginas de uso diário.

Funciona com~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/anhlamtruong/living-ui-skills/tree/HEAD/skills/living-ui-swarm

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Prévia

Do README da skill

living-ui-swarm preview

Documentação

Animação de construção por agrupamento de agentes

Um canvas overlay fixo desenha uma pequena equipe de cursores que "constroem" cada seção de uma página quando ela entra na viewport. O conteúdo é HTML real, renderizado no servidor o tempo todo. O motor apenas oculta itens por alguns segundos e executa uma coreografia curta para cada um.

Exemplo ao vivo: https://www.lamanhtruong.com. Seu código-fonte está fixado em portfolio-anh@a5d1651. assets/swarm/ contém o mesmo motor, limpo para reutilização.

Quando usar e quando não usar

Use onde a própria página é o argumento de venda: um portfólio, uma página de lançamento ou de história do produto, um estudo de caso, uma página "sobre nossa IA". As pessoas visitam uma vez, rolam do topo ao fim, e a construção conta algo a elas ("esta pessoa entrega com agentes").

Ignore para qualquer coisa que as pessoas usam todos os dias: painéis, documentação, configurações, checkout, leitura longa. A construção custa alguns segundos por seção, e um usuário que retorna não tem motivo para assistir duas vezes. É por isso que o motor lembra de uma construção concluída por aba (sessionStorage) e nunca a repete ao recarregar.

O que custa:

  • Cerca de 5.000 linhas de TypeScript. A camada de construção é carregada com next/dynamic após a hidratação, então a primeira pintura nunca espera por ela; as peças de rolagem suave e parallax (Lenis, GSAP, ScrollTrigger) carregam com a página. Um Next.js 15 novo com todas as quatro skills living-ui chegou a cerca de 67 kB de JS de rota (170 kB na primeira carga).
  • Um pequeno escore de layout-shift enquanto o log ao vivo preenche (cerca de 0,03 em uma página de teste, bem abaixo da linha "boa" de 0,1): novas linhas de log se movem dentro de sua caixa de altura fixa. O resto da página nunca se desloca.
  • Um canvas fixo extra e um loop gsap.ticker. O loop para quando nada está se movendo.
  • GSAP 3.13+ (gratuito, incluindo ScrollTrigger), @gsap/react, Lenis.

Como funciona em um parágrafo

Um pequeno script inline em <head> define html[data-build="on"] antes da primeira pintura, e o CSS então oculta cada [data-build-item] não construído com opacity: 0. Opacidade em vez de display ou visibilidade não causa mudança de layout e nada é removido da árvore de acessibilidade. Após a hidratação, o motor escaneia a página em um grafo de tarefas por [data-build-section], arma um ScrollTrigger para cada seção e, quando uma entra na viewport, executa plano → construção → revisão:

  • Plano: um planejador desenha guias e distribui tarefas.
  • Construção: trabalhadores reivindicam tarefas por distância, função e caminho crítico, e cada tarefa executa uma timeline do GSAP.
  • Revisão: um revisor delineia a seção, marca uma lista de verificação e a sela.

Itens e seções concluídos recebem [data-built]. Se o motor nunca iniciar, o script de cabeçalho remove a flag após 3 s para que nada permaneça oculto.

Arquitetura, fluxo de dados e como estender: references/architecture.md.

Instalação (Next.js App Router)

  1. Dependências. Testado com Next.js 15.1 + React 18.3 (o exemplo trabalhado) e Next.js 15.5 + React 19.1 + Tailwind 4 (um create-next-app novo), TypeScript 5.8–5.9, [email protected]–3.15, @gsap/[email protected], [email protected]:

    npm i gsap@^3.13 @gsap/react@^2.1 lenis@^1.3
    
  2. Copie o motor. Copie assets/swarm/ para src/components/swarm/. As importações dentro dele são relativas, então qualquer pasta funciona.

  3. Adicione o script de flag ao <head> do layout raiz. Ele define um atributo em <html> antes do React hidratar, então adicione suppressHydrationWarning a <html>:

    // app/layout.tsx
    import { BuildFlagScript } from "@/components/swarm/build-flag-script";
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en" suppressHydrationWarning>
          <head>
            <BuildFlagScript />
          </head>
          <body>{children}</body>
        </html>
      );
    }
    
  4. Monte o motor uma vez, nas páginas que o usam, após o conteúdo:

    // app/page.tsx (um componente servidor; permanece estático)
    import "@/components/swarm/swarm.css";
    import { SwarmRoot } from "@/components/swarm/swarm-root";
    
    export default function Page() {
      return (
        <>
          <main>{/* seções marcadas com data-build-* */}</main>
          <SwarmRoot />
        </>
      );
    }
    

    SwarmRoot também monta a rolagem suave Lenis orientada por gsap.ticker. Se o aplicativo já executa Lenis, exclua <SmoothScroll /> de swarm-root.tsx. build-layer.tsx lê a instância através de useLenis(), que precisa de uma instância root do ReactLenis em algum lugar da página (<ReactLenis root />, como smooth-scroll.tsx monta). Não precisa ser um pai.

  5. Mantenha a rota estática. O motor apenas decora HTML que já existe. Não chame cookies(), headers() ou auth() na árvore de layout da página apenas para ler uma sessão. Busque isso no cliente, e a página pode ser pré-renderizada e servida pelo CDN. Em páginas que não montam SwarmRoot, o script de flag é inofensivo: nenhum elemento carrega data-build-item, e o fail-safe limpa a flag.

  6. Personalize o tema. Defina variáveis CSS (--swarm-font-mono, --swarm-accent, --swarm-text, --swarm-muted, --swarm-dim, --swarm-line, --swarm-neutral-text|fill|stroke), então edite COLORS e ROLE_COLOR em palette.ts. O canvas não consegue ler variáveis CSS de forma barata a cada quadro, então sua paleta vive em TS.

Marque a página

Tudo é orientado por atributos de dados na marcação renderizada no servidor. A referência completa, com padrões e comportamento para celular, está em references/dom-contract.md. Receitas de copiar e colar para hero, grade de cartões, lista, imagem ou vídeo, faixa de métricas e final estão em references/recipes.md.

AtributoEmSignificado
data-build-section="nome"wrapper da seçãoUma unidade de construção com seu próprio plano, equipe e revisão
data-build-start="load"seçãoConstruir logo após a inicialização em vez de na rolagem, com o orçamento maior do hero
data-build-item + data-build="frame|type|reveal|image|dup"elementoUma tarefa desse tipo
data-build-listcontainer de listaSeu primeiro item é o mestre (frame); irmãos dup são copiados dele
data-build-gridcontainer ou a seçãoUm agente de layout desenha a grade e enquadra cada célula primeiro
data-build-font / -weight / -scaleitens typeRascunho mono e troca de fonte; varredura de peso; começar pequeno, depois redimensionar e mover
data-build-color="text|fill|stroke"qualquer itemRoda de cores escolhe a cor real após o item aparecer
data-build-tuneframeAbre o inspetor de raio e preenchimento
data-build-from="left|right|top|bottom"imageBorda de onde a imagem é arrastada
data-build-keep / ="mobile"qualquer itemNunca oculto (apenas celular com mobile), ainda anotado
data-build-label, -order, -rolequalquer itemTexto da etiqueta, dica de ordenação, qual função o assume
data-swarm-spawn="role"<g> SVG no heroNós de malha de onde a equipe sai na inicialização (≥1024 px)
data-swarm-logcontainerLinhas de fallback renderizadas no servidor são substituídas por um log ao vivo
data-build-finale + data-deploy-target + data-deploy-textúltima seção, seu CTAImplantador "entrega" a página após a última revisão
?build=off / ?build=forceURLDesabilitar para esta visualização / repetir mesmo se esta aba já assistiu

O motor define: html[data-build="on"] enquanto ativo, [data-built] em itens e seções concluídos, [data-lit] em nós de malha durante a inicialização e [data-deployed] no alvo do final.

Regras que o mantêm funcionando:

  • Tamanho da seção. Cerca de uma viewport de itens por seção. Divida páginas longas: uma seção por trabalho em uma linha do tempo, uma por cartão em uma grade grande, uma seção separada para um cabeçalho de seção. Seções pequenas terminam dentro de seu orçamento e parecem deliberadas; enormes caem na velocidade de recuperação.
  • Aninhamento. Um item aninhado dentro de outro item espera pelo pai. Marque o cartão como frame e seu título, texto e chips dentro dele.
  • Digitação. Use type apenas para texto curto (abaixo de cerca de 120 caracteres). Texto mais longo automaticamente cai para reveal. A digitação lê caixas de glifo uma vez, então texto gerenciado pelo React que muda após a hidratação (contadores, contagens de visualização) deve ser um frame, não type.
  • LCP. Mantenha o título principal visível em celulares com data-build-keep="mobile". Em celulares, o título do hero é o Largest Contentful Paint, e ocultá-lo custa Core Web Vitals. Desktop ainda recebe a construção completa com troca de fonte.
  • Log ao vivo. O container data-swarm-log deve ser renderizado no servidor com linhas de fallback reais e uma altura fixa (.swarm-log). O motor as substitui por linhas ao vivo e as coloca de volta ao pular ou desmontar.

Hero mínimo:

<section data-build-section="hero" data-build-start="load">
  <p data-build-item data-build="type">sua linha de apoio</p>
  <h1
    data-build-item data-build="type" data-build-font data-build-weight
    data-build-keep="mobile" data-build-order="-1" data-build-role="coder"
  >
    Seu Nome <span className="serif">(Acento)</span>
  </h1>
  <div data-build-item data-build="image" data-build-from="right" className="avatar">
    <img src="/me.jpg" alt="Retrato" />
  </div>
  {/* next/image também funciona (renderiza um <img> interno que o motor lê). */}
  <div data-build-list className="buttons">
    <a data-build-item data-build="frame" href="#">GitHub</a>
    <a data-build-item data-build="dup" href="#">LinkedIn</a>
    <a data-build-item data-build="dup" data-build-color="fill" href="#">Currículo</a>
  </div>
</section>

Regras de suavidade

Elas vieram de observar construções reais. Cada uma corrigiu um problema visível.

  • Nunca force o término em um prazo. Uma versão inicial pulava cada timeline em execução para 5× de velocidade e colocava os itens não reivindicados no prazo de cada seção. As pessoas viam como um solavanco no final de cada seção. Orçamentos agora são alvos de planejamento.
    • estimate() escala as durações das tarefas para que o plano se encaixe.
    • Quando uma seção atrasa, pace() aumenta suavemente um fator de recuperação, no máximo 1,7× ao longo de 0,7 s.
    • Cada tween permanece pelo menos 120 ms de duração mesmo na recuperação total, e passos de digitação permanecem pelo menos 16 ms. Nada surge.
    • Se precisar mais rápido, torne os cursores mais rápidos (speed em palette.ts). Não corte animações.
  • Anime apenas opacidade, clip-path, filtro e transform. Sem escritas de layout. Rascunhos de troca de fonte são clones em uma camada fora do fluxo, e a tarefa de escala remove sua transformação inline no final.
  • Um relógio. A simulação e o desenho do canvas rodam em um único callback gsap.ticker. Mudanças de estado do React ocorrem apenas quando a construção inicia ou para, nunca por quadro. O ticker para quando nenhum agente está na tela.
  • Meça adiante. Seções até cerca de duas viewports abaixo da dobra são medidas em fatias requestIdleCallback. Uma seção iniciando nunca causa uma grande rajada de leitura de layout no quadro de rolagem.
  • Respeite o leitor.
    • Focar qualquer coisa dentro de uma seção não construída termina essa seção imediatamente, para que usuários de teclado nunca esperem.
    • Um redimensionamento no meio da construção finaliza as seções em execução, porque seus retângulos medidos estão desatualizados.
    • Uma aba oculta pausa tudo.
    • O HUD sempre oferece Pular, Repetir e Inspecionar.
  • Celulares recebem menos. Cada perfil tem seu próprio orçamento, equipe e detalhe (detectProfile() em palette.ts).
    • Celulares: 8 agentes, cerca de 2,2 s por seção, uma troca de fonte por página, uma escolha de cor por seção, uma varredura de duplicação por lista, sem painéis de inspetor, sem tarefa de escala, sem implantador.
    • Tablets: 11 agentes.
    • Desktop: 15 agentes, cerca de 3,9 s por seção, 5,25 s para o hero.

Botões para "mais rápido", "mais calmo" e "mais agentes": references/tuning.md.

Movimento reduzido

O motor executa para cada visitante, incluindo pessoas cujo sistema operacional pede movimento reduzido. O proprietário do site decidiu que a construção é o ponto da página, e o HUD sempre oferece Pular, então esta é uma escolha deliberada, não um padrão para copiar cegamente. Para respeitar a configuração, faça todas essas três alterações:

// build-flag-script.tsx: dentro de CODE, antes de d.setAttribute('data-build','on')
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;

// motion.ts
export function offByUrl(): boolean {
  return OFF_RE.test(window.location.search) ||
    window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}

// smooth-scroll.tsx
respectReducedMotion: true,

Teste de ambas as maneiras: alguns navegadores (e algumas configurações de automação) relatam reduce mesmo quando ninguém escolheu, o que é uma razão pela qual o padrão aqui é "ligado".

Trabalhando com outros efeitos

  • Porte em [data-built]. O motor lê retângulos de elemento e alterna opacidade e clip-path durante a construção. Tilt, glitch, scrambled, parallax em conteúdo, ou qualquer outra coisa que transforme um item corromperá essas medições se executar cedo. Inicie tais efeitos apenas quando o item e sua seção carregarem [data-built], ou quando html[data-build] não for "on". Um auxiliar pronto:

    export function isBuilt(el: Element): boolean {
      if (document.documentElement.getAttribute("data-build") !== "on") return true;
      const item = el.closest("[data-build-item]");
      if (item && !item.hasAttribute("data-built")) return false;
      const section = el.closest("[data-build-section]");
      return !section || section.hasAttribute("data-built");
    }
    
  • Vídeos. Um item image que envolve um <video poster> é arrastado usando seu quadro de pôster como miniatura. Dê ao wrapper um data-build-label como clip.mp4, porque não há <img> para ler um nome de arquivo.

  • Parallax decorativo é bom em qualquer lugar onde move apenas decoração. Nunca coloque em algo que o motor mede.

Faça seu próprio

O motor aqui é uma implementação original, e o mesmo padrão se aplica a qualquer coisa que você construir a partir dele. Se um site te inspirou, escreva o que torna aquele site reconhecível e fique longe exatamente dessas coisas. Pegar emprestado ideias é diferente de copiar a aparência. Se você construir algo que parece com o exemplo, mude o suficiente para que ninguém confunda os dois.

Referências

  • references/architecture.md - Arquitetura e fluxo de dados
  • references/dom-contract.md - Contrato DOM completo com valores padrão
  • references/recipes.md - Receitas de copiar e colar para elementos comuns
  • references/tuning.md - Botões de ajuste para velocidade, calma e número de agentes
  • references/faq.md - Perguntas frequentes e solução de problemas

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Habilidades Relacionadas