HyperFrames
O HTML é a fonte da verdade para o 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. A estrutura gerencia a visibilidade dos clipes, a reprodução de mídia e a 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 ainda não definiu uma direção, entenda a intenção antes de escolher as cores:
- Público — quem assiste? Desenvolvedores? Executivos? Consumidores em geral?
- Plataforma — onde será exibido? Redes sociais (15s), site principal, demonstração de produto, interno?
- Prioridade — o que é mais importante? Qualidade do movimento? Precisão do conteúdo? Fidelidade à marca? Velocidade?
- Variações — o usuário quer opções ou uma única abordagem ideal?
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 com diferenças significativas — não apenas trocas de cores, mas diferentes ritmos, níveis de energia ou abordagens estruturais. Uma segura/esperada, uma ambiciosa. Não é 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 maiúsculas/minúsculas — 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 encontra localmente (sem diretório fonts/ com arquivos .woff2, nem uma fonte embutida), avise o usuário antes de escrever HTML: "design.md especifica a fonte [nome da fonte], mas nenhum arquivo de fonte foi encontrado. Adicione arquivos .woff2 ao diretório fonts/ ou usarei como fallback [alternativa embutida mais próxima]."
Se nenhum design.md existir, 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 opções visualmente? → Execute o seletor de design: leia references/design-picker.md para o fluxo completo. Isso exibe uma página de seletor visual. O usuário configura clima, paleta, tipografia e movimento no navegador, depois copia o design.md gerado e 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 as cores da marca em escala apropriada para vídeo — não na opacidade de UI web.
Passo 2: Expansão do prompt
Sempre execute em cada composição (exceto peças de cena única e edições triviais). Esta etapa fundamenta a intenção do usuário em relação ao design.md e house-style.md e produz um intermediário consistente que todos os agentes downstream leem da mesma forma.
Leia references/prompt-expansion.md para o processo completo e formato de saída.
Passo 3: Planejar
Antes de escrever HTML, pense em alto nível:
- O quê — o que o espectador deve experienciar? Identifique o arco narrativo, os momentos-chave e os batimentos emocionais.
- Estrutura — quantas composições, quais são sub-composições vs. inline, quais trilhas carregam o quê (vídeo, áudio, sobreposições, legendas).
- Ritmo — declare o ritmo da cena antes de implementar. Quais cenas são rápidas, quais são pausadas, onde os shaders chegam, 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. Uma solicitação de "um cartão de título" não é uma solicitação 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 a temporização, adicionar um elemento), vá direto para as regras.
Layout Antes da Animação
Posicione cada elemento onde ele deve estar no seu momento mais visível — o quadro onde ele entrou completamente, está corretamente posicionado e ainda não está saindo. Escreva isso como HTML+CSS estático primeiro. Ainda sem GSAP.
Por que isso importa: Se você posicionar os elementos no estado inicial animado (fora da tela, escalados para 0, opacidade 0) e fizer um tween 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. Ao construir o estado final primeiro, você pode ver e corrigir problemas de layout antes de adicionar qualquer movimento.
O processo
- Identifique o quadro herói 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 absoluta; o tween descreve a jornada para chegar lá. (Em sub-composições carregadas viadata-composition-src, prefiragsap.fromTo()— veja regras GSAP de suporte 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 o 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: Animar PARA 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: Animar 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 herói. 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 com empilhamento z (pilhas de cartões, camadas de profundidade) são intencionais. A etapa de layout serve para capturar sobreposições não intencionais — dois títulos caindo um em cima do outro, uma estatística cobrindo um rótulo, conteúdo saindo 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 usa a duração da mídia como padrão. |
data-track-index | Sim | Inteiro. Clipes na mesma trilha não podem se sobrepor. |
data-media-start | Não | Deslocamento de corte na origem (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 inicial (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-composição |
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 da Composição
Sub-composições carregadas via data-composition-src usam um wrapper <template>. Composições independentes (o index.html principal) NÃO usam <template> — elas colocam a 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 de origem.
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-composições).
<!doctype html>
<html
data-composition-variables='[
{"id":"title","type":"string","label":"Title","default":"Hello"},
{"id":"theme","type":"enum","label":"Theme","default":"light","options":[
{"value":"light","label":"Light"},
{"value":"dark","label":"Dark"}
]}
]'
>
<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>
# A pré-visualização de desenvolvimento usa os padrões declarados
npx hyperframes preview
# Renderizar com substituições
npx hyperframes render --variables '{"title":"Q4 Report","theme":"dark"}' --output q4.mp4
# Ou a partir 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-composições 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":"$29"}'
></div>
<div
data-composition-id="card-enterprise"
data-composition-src="compositions/card.html"
…
[truncado para tradução]