HyperFrames
HTML é a fonte da verdade para vídeo. Uma composição é um arquivo HTML com atributos data-* para temporização, uma timeline GSAP para animação e CSS para aparência. O framework gerencia a visibilidade dos clipes, reprodução de mídia e sincronização da timeline.
Abordagem
Descoberta (apenas para solicitações exploratórias)
Para solicitações abertas ("faça um vídeo de lançamento de produto", "crie algo para nossa marca") onde o usuário não se comprometeu com uma direção, entenda a intenção antes de escolher cores:
- Público — quem assiste? Desenvolvedores? Executivos? Consumidores em geral?
- Plataforma — onde é reproduzido? Rede social (15s), hero do site, demo de produto, interno?
- Prioridade — o que importa mais? Qualidade do movimento? Precisão do conteúdo? Fidelidade da marca? Velocidade?
- Variações — o usuário quer opções ou uma única melhor versão?
Para solicitações específicas ("adicione um cartão de título", "corrija a temporização da cena 3"), pule a descoberta.
Para solicitações exploratórias, considere oferecer 2-3 variações que diferem significativamente — não apenas trocas de cor, mas diferentes ritmos, níveis de energia ou abordagens estruturais. Uma segura/esperada, uma ambiciosa. Não torne isso obrigatório — é uma ferramenta disponível quando apropriado.
Passo 1: Sistema de design
Se design.md ou DESIGN.md existir no projeto, leia-o primeiro (verifique ambas as capitalizações — são arquivos diferentes no Linux). É a fonte da verdade para cores da marca, fontes e restrições. Use seus valores exatos — não invente cores ou substitua fontes. Qualquer formato funciona (frontmatter YAML, prosa, tabelas — apenas extraia os valores).
Se nomear fontes que você não pode encontrar localmente (sem diretório fonts/ com arquivos .woff2, não é uma fonte embutida), avise o usuário antes de escrever HTML: "design.md especifica [nome da fonte] mas nenhum arquivo de fonte foi encontrado. Adicione arquivos .woff2 em fonts/ ou vou usar [alternativa embutida mais próxima] como fallback."
Se não houver design.md, ofereça ao usuário uma escolha:
- O usuário nomeou um estilo ou clima? → Leia visual-styles.md para os 8 presets nomeados. Escolha o mais próximo.
- Quer navegar pelas opções visualmente? → Execute o seletor de design: leia references/design-picker.md para o fluxo de trabalho completo. Isso serve uma página visual de seletor. O usuário configura clima, paleta, tipografia e movimento no navegador, depois copia o design.md gerado e o cola de volta na conversa.
- Quer pular e ir rápido? → Pergunte: clima, claro ou escuro, alguma cor/fonte da marca? Depois escolha uma paleta de house-style.md.
design.md define a marca. Ele não define regras de composição de vídeo. Essas vêm de references/video-composition.md e house-style.md. Use cores da marca em escala apropriada para vídeo — não na opacidade de UI web.
Passo 2: Expansão de prompt
Sempre execute em toda composição (exceto peças de uma única cena e edições triviais). Esta etapa ancora a intenção do usuário em relação a design.md e house-style.md e produz um intermediário consistente que todo agente downstream lê da mesma forma.
Leia references/prompt-expansion.md para o processo completo e formato de saída.
Passo 3: Planejamento
Antes de escrever HTML, pense em alto nível:
- O quê — o que o espectador deve experimentar? Identifique o arco narrativo, momentos-chave e batidas emocionais.
- Estrutura — quantas composições, quais são sub-composições versus inline, quais trilhas carregam o quê (vídeo, áudio, sobreposições, legendas).
- Ritmo — declare o ritmo da sua cena antes de implementar. Quais cenas são curtas, quais são longas, onde os shaders aparecem, onde a energia atinge o pico. Nomeie o padrão: rápido-rápido-LENTO-rápido-SHADER-pausa. Leia references/beat-direction.md para modelos de ritmo.
- Temporização — quais clipes determinam a duração, onde as transições ocorrem, qual é o ritmo.
- Layout — construa o estado final primeiro. Veja "Layout Antes da Animação" abaixo.
- Animar — depois adicione movimento usando as regras abaixo.
Construa o que foi solicitado. Um pedido de "um cartão de título" não é um pedido de "um cartão de título + 3 cenas de apoio + música ambiente + legendas." Cada cena, cada elemento, cada tween deve merecer seu lugar. Se cenas ou elementos adicionais melhorariam genuinamente a peça, proponha-os — não os adicione.
Para pequenas edições (corrigir uma cor, ajustar temporização, adicionar um elemento), vá direto para as regras.
Layout Antes da Animação
Posicione cada elemento onde ele deve estar em seu momento mais visível — o quadro onde ele está totalmente entrado, corretamente posicionado e ainda não saindo. Escreva isso como HTML+CSS estático primeiro. Ainda sem GSAP.
Por que isso importa: Se você posicionar elementos em seu estado inicial animado (fora da tela, escalados para 0, opacidade 0) e aplicar tweens para onde você acha que eles devem chegar, você está adivinhando o layout final. Sobreposições são invisíveis até o vídeo ser renderizado. Construindo o estado final primeiro, você pode ver e corrigir problemas de layout antes de adicionar qualquer movimento.
O processo
- Identifique o quadro hero para cada cena — o momento em que o maior número de elementos está simultaneamente visível. Este é o layout que você constrói.
- Escreva CSS estático para esse quadro. O contêiner
.scene-contentDEVE preencher a cena inteira usandowidth: 100%; height: 100%; padding: Npx;comdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Use padding para empurrar o conteúdo para dentro — NUNCA useposition: absolute; top: Npxem um contêiner de conteúdo. Contêineres de conteúdo com posicionamento absoluto transbordam quando o conteúdo é mais alto que o espaço restante. Reserveposition: absoluteapenas para elementos decorativos. - Adicione entradas com
gsap.from()— anime DE fora da tela/invisível PARA a posição CSS. A posição CSS é a verdade fundamental; o tween descreve a jornada para chegar lá. (Em sub-composições carregadas viadata-composition-src, prefiragsap.fromTo()— veja regras GSAP de sustentação de carga em references/motion-principles.md.) - Adicione saídas com
gsap.to()— anime PARA fora da tela/invisível DA posição CSS.
Exemplo
/* scene-content preenche a cena, padding posiciona o conteúdo */
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 24px;
box-sizing: border-box;
}
.title {
font-size: 120px;
}
.subtitle {
font-size: 42px;
}
/* Contêiner preenche qualquer tamanho de cena (1920x1080, 1080x1920, etc).
Padding posiciona o conteúdo. Flex + gap lida com espaçamento. */
ERRADO — dimensões fixas e posicionamento absoluto:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Passo 3: Anime ATÉ essas posições
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// Passo 4: Anime PARA FORA dessas posições
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
Quando elementos compartilham espaço ao longo do tempo
Se o elemento A sai antes do elemento B entrar na mesma área, ambos devem ter posições CSS corretas para seus respectivos quadros hero. A ordenação da timeline garante que eles nunca coexistam visualmente — mas se você pular a etapa de layout, não perceberá o caso em que eles acidentalmente se sobrepõem devido a um erro de temporização.
O que conta como sobreposição intencional
Efeitos em camadas (brilho atrás do texto, elementos de sombra, padrões de fundo) e designs empilhados em z (pilhas de cartões, camadas de profundidade) são intencionais. A etapa de layout é para capturar sobreposição não intencional — dois títulos caindo um em cima do outro, uma estatística cobrindo um rótulo, conteúdo vazando para fora do quadro.
Atributos de Dados
Todos os Clipes
| Atributo | Obrigatório | Valores |
|---|---|---|
id | Sim | Identificador único |
data-start | Sim | Segundos ou referência de ID de clipe ("el-1", "intro + 2") |
data-duration | Obrigatório para img/div/composições | Segundos. Vídeo/áudio padrão para duração da mídia. |
data-track-index | Sim | Inteiro. Clipes na mesma trilha não podem se sobrepor. |
data-media-start | Não | Offset de corte na fonte (segundos) |
data-volume | Não | 0-1 (padrão 1) |
data-track-index não afeta a camada visual — use CSS z-index.
Clipes de Composição
| Atributo | Obrigatório | Valores |
|---|---|---|
data-composition-id | Sim | ID único da composição |
data-start | Sim | Tempo de início (composição raiz: use "0") |
data-duration | Sim | Tem precedência sobre a duração da timeline GSAP |
data-width / data-height | Sim | Dimensões em pixels (1920x1080 ou 1080x1920) |
data-composition-src | Não | Caminho para arquivo HTML externo |
data-variable-values | Não | Objeto JSON de substituições de variáveis por instância em um host de sub-comp |
No elemento <html> raiz:
| Atributo | Obrigatório | Valores |
|---|---|---|
data-composition-variables | Não | Array JSON de variáveis declaradas (id/type/label/default) — impulsiona a UI de edição do Studio e fornece padrões para getVariables() |
Estrutura de Composição
Sub-composições carregadas via data-composition-src usam um wrapper <template>. Composições independentes (o main index.html) NÃO usam <template> — elas colocam o div data-composition-id diretamente no <body>. Usar <template> em um arquivo independente esconde todo o conteúdo do navegador e quebra a renderização.
Estrutura de sub-composição:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- conteúdo -->
<style>
[data-composition-id="my-comp"] {
/* estilos escopados */
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// tweens...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
Carregue na raiz: <div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>
Variáveis (Composições Parametrizadas)
Renderize a mesma composição com conteúdo diferente — título, cor do tema, preços, legendas — sem editar o HTML fonte.
Padrão de três etapas:
- Declare variáveis na raiz
<html>da composição comdata-composition-variables. Cada entrada precisa deid,type(um destring,number,color,boolean,enum),labeledefault. Entradas enum também precisam deoptions: [{value, label}, ...]. - Leia os valores resolvidos dentro do script da composição com
window.__hyperframes.getVariables(). Retorna o resultado mesclado de padrões declarados + substituições por instância + substituições CLI. - Substitua no momento da renderização com
npx hyperframes render --variables '{...}'(nível superior) ou comdata-variable-values='{...}'no elemento host (por instância para sub-comps).
<!doctype html>
<html
data-composition-variables='[
{"id":"title","type":"string","label":"Título","default":"Olá"},
{"id":"theme","type":"enum","label":"Tema","default":"light","options":[
{"value":"light","label":"Claro"},
{"value":"dark","label":"Escuro"}
]}
]'
>
<body>
<div data-composition-id="root" data-width="1920" data-height="1080">
<h1 id="hero" class="clip" data-start="0" data-duration="3"></h1>
<script>
const { title, theme } = window.__hyperframes.getVariables();
document.getElementById("hero").textContent = title;
document.body.dataset.theme = theme;
</script>
</div>
</body>
</html>
# Pré-visualização de desenvolvimento usa padrões declarados
npx hyperframes preview
# Renderize com substituições
npx hyperframes render --variables '{"title":"Relatório Q4","theme":"dark"}' --output q4.mp4
# Ou de um arquivo JSON
npx hyperframes render --variables-file ./vars.json
Valores por instância em sub-composição: o mesmo getVariables() funciona dentro de sub-comps carregadas via data-composition-src. Cada elemento host passa seus próprios valores:
<div
data-composition-id="card-pro"
data-composition-src="compositions/card.html"
data-variable-values='{"title":"Pro","price":"R$29"}'
></div>
<div
data-composition-id="card-enterprise"
data-composition-src="compositions/card.html"
…
[truncado para tradução]