HyperFrames
HTML é a fonte de verdade para vídeo. Uma composição é um arquivo HTML com atributos data-* para tempo, uma timeline GSAP para animação e CSS para aparência. O framework gerencia a visibilidade dos clipes, a reprodução de mídia e a sincronização da timeline.
Abordagem
Antes de escrever HTML, pense em alto nível:
- O quê — o que o espectador deve experimentar? Identifique o arco narrativo, os momentos-chave e os batimentos emocionais.
- Estrutura — quantas composições, quais são sub-composições vs inline, quais faixas carregam o quê (vídeo, áudio, sobreposições, legendas).
- Tempo — 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.
Para pequenas edições (corrigir uma cor, ajustar tempo, adicionar um elemento), vá direto para as regras.
Portão de Identidade Visual
Verifique nesta ordem:
- Existe um DESIGN.md no projeto? → Leia-o. Use suas cores, fontes, regras de movimento e restrições "O que NÃO fazer" exatas.
- Existe um visual-style.md? → Leia-o. Aplique seus
style_prompt_fulle campos estruturados. (Nota:visual-style.mdé um arquivo específico do projeto.visual-styles.mdé a biblioteca de estilos com 8 modelos nomeados — são arquivos diferentes.) - O usuário nomeou um estilo (ex.: "Swiss Pulse", "escuro e tecnológico", "marca de luxo")? → Leia visual-styles.md para os 8 modelos nomeados. Gere um DESIGN.md mínimo com:
## Style Prompt(um parágrafo),## Cores(3-5 valores hexadecimais com funções),## Tipografia(1-2 famílias de fontes),## O que NÃO fazer(3-5 anti-padrões). - Nenhum dos casos acima? → Faça 3 perguntas antes de escrever qualquer HTML:
- Qual é o clima? (explosivo / cinematográfico / fluido / técnico / caótico / acolhedor)
- Tela clara ou escura?
- Alguma cor, fonte ou referência visual específica da marca? Em seguida, gere um DESIGN.md mínimo a partir das respostas.
Toda composição deve rastrear sua paleta e tipografia de volta a um DESIGN.md, visual-style.md ou direção explícita do usuário. Se você está usando #333, #3b82f6 ou Roboto — você pulou esta etapa.
Para padrões de movimento, dimensionamento, padrões de entrada e easing — siga house-style.md. O estilo da casa lida com COMO as coisas se movem. O DESIGN.md lida com a APARÊNCIA das coisas.
Layout Antes da Animação
Posicione cada elemento onde ele deve estar em 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 elementos em seu estado inicial animado (fora da tela, escalado para 0, opacidade 0) e animá-los até onde você acha que eles devem chegar, você está adivinhando o layout final. Sobreposições são invisíveis até o vídeo renderizar. 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á. - 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 lidam 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 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: 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 heróis. 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 tempo.
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. O objetivo da etapa de layout é detectar sobreposições não intencionais — dois títulos caindo um em cima do outro, uma estatística cobrindo uma legenda, 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 do 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 faixa não podem se sobrepor. |
data-media-start | Não | Deslocamento 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 de 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 |
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 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 da 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 com escopo */
}
</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>
Carregar 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>
Vídeo e Áudio
O vídeo deve estar muted playsinline. O áudio é sempre um elemento <audio> separado:
<video
id="el-v"
data-start="0"
data-duration="30"
data-track-index="0"
src="video.mp4"
muted
playsinline
></video>
<audio
id="el-a"
data-start="0"
data-duration="30"
data-track-index="2"
src="video.mp4"
data-volume="1"
></audio>
Contrato da Timeline
- Todas as timelines começam
{ paused: true }— o player controla a reprodução - Registre cada timeline:
window.__timelines["<id-da-composição>"] = tl - O framework aninha automaticamente sub-timelines — NÃO as adicione manualmente
- A duração vem de
data-duration, não do comprimento da timeline GSAP - Nunca crie tweens vazios para definir a duração
Regras (Não-Negociáveis)
Determinístico: Sem Math.random(), Date.now() ou lógica baseada em tempo. Use um PRNG com semente se precisar de valores pseudo-aleatórios (ex.: mulberry32).
GSAP: Animar apenas propriedades visuais (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, transformações). NÃO anime visibility, display, ou chame video.play()/audio.play().
Conflitos de animação: Nunca anime a mesma propriedade no mesmo elemento de múltiplas timelines simultaneamente.
Sem repeat: -1: Timelines com repetição infinita quebram o mecanismo de captura. Calcule a contagem exata de repetições a partir da duração da composição: repeat: Math.ceil(duration / cycleDuration) - 1.
Construção síncrona da timeline: Nunca construa timelines dentro de async/await, setTimeout ou Promises. O mecanismo de captura lê window.__timelines de forma síncrona após o carregamento da página. As fontes são incorporadas pelo compilador, então estão disponíveis imediatamente — não há necessidade de esperar pelo carregamento de fontes.
Nunca faça:
- Esquecer o registro de
window.__timelines - Usar vídeo para áudio — sempre vídeo mudo +
<audio>separado - Aninhar vídeo dentro de um div temporizado — use um wrapper não temporizado
- Usar
data-layer(usedata-track-index) oudata-end(usedata-duration) - Animar dimensões do elemento de vídeo — anime um div wrapper
- Chamar play/pause/seek em mídia — o framework controla a reprodução
- Criar um contêiner de nível superior sem
data-composition-id - Usar
repeat: -1em qualquer timeline ou tween — sempre repetições finitas - Construir timelines de forma assíncrona (dentro de
async,setTimeout,Promise) - Usar
gsap.set()em elementos de clipe de cenas posteriores — eles não existem no DOM no carregamento da página. Usetl.set(selector, vars, timePosition)dentro da timeline no momento ou após odata-startdo clipe. - Usar
<br>em texto de conteúdo — quebras de linha forçadas não levam em conta a largura real da fonte renderizada. Texto que quebra naturalmente + um<br>produz uma quebra extra indesejada, causando sobreposição. Deixe o texto quebrar viamax-widthem vez disso. Exceção: títulos curtos de exibição onde cada palavra está deliberadamente em sua própria linha (ex.: "O JOGO IMORTAL" em 130px).
Transições de Cena (Não-Negociáveis)
Toda composição com várias cenas DEVE seguir TODAS estas regras. Violar qualquer uma delas resulta em uma composição quebrada.
- SEMPRE use transições entre cenas. Sem cortes secos. Sem exceções.
- SEMPRE use animações de entrada em cada cena. Cada elemento anima ENTRANDO via
gsap.from(). Nenhum elemento pode aparecer totalmente formado. Se uma cena tem 5 elementos, ela precisa de 5 tweens de entrada. - NUNCA use animações de saída exceto na cena final. Isso significa: NENHUM
gsap.to()que anime opacidade para 0, y para fora da tela, escala para 0, ou qualquer outra animação de "saída" antes de uma transição ser acionada. A transição É a saída. O conteúdo da cena de saída DEVE estar totalmente visível no momento em que a transição começa. - Apenas na cena final: A última cena pode esmaecer elementos (ex.: fade para preto). Esta é a ÚNICA cena onde
gsap.to(..., { opacity: 0 })é permitido.
ERRADO — animação de saída antes da transição:
// PROIBIDO — isso esvazia a cena antes que a transição possa usá-la
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// transição é acionada em um quadro vazio
CERTO — apenas entrada, transição lida com a saída:
// Animações de entrada da Cena 1
tl.from("#s1-title", { y: 50, opacity: 0, duration: 0.7, ease: "power3.out" }, 0.3);
tl.from("#s1-subtitle", { y: 30, opacity: 0, duration: 0.5, ease: "power2.out" }, 0.6);
// SEM tweens de saída — a transição em 7.2s lida com a mudança de cena
// Animações de entrada da Cena 2
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
Diretrizes de Animação
- Desloque a primeira animação em 0.1-0.3s (não t=0)
- Varie os eases entre os tweens de entrada — use pelo menos 3 eases diferentes por cena
- Não repita um padrão de entrada dentro de uma cena
- Evite gradientes lineares em tela cheia em fundos escuros (bandeamento H.264 — use radial ou sólido + brilho localizado)
- Títulos com 60px+, corpo com 20px+, rótulos de dados com 16px+ para vídeo renderizado
font-variant-numeric: tabular-numsem colunas de números
Quando nenhum visual-style.md ou direção de animação for fornecida, siga house-style.md para padrões estéticos.
Tipografia e Ativos
- Fontes: Basta escrever o
font-familyque você deseja no CSS — o compilador incorpora automaticamente fontes suportadas. Se uma fonte não for suportada, o compilador avisa. - Adicione
crossorigin="anonymous"em mídia externa