HyperFrames
HTML es la fuente de verdad para el video. Una composición es un archivo HTML con atributos data-* para el tiempo, una línea de tiempo GSAP para la animación y CSS para la apariencia. El framework maneja la visibilidad de los clips, la reproducción multimedia y la sincronización con la línea de tiempo.
Enfoque
Descubrimiento (solo para solicitudes exploratorias)
Para solicitudes abiertas ("hazme un video de lanzamiento de producto", "crea algo para nuestra marca") donde el usuario no se ha comprometido con una dirección, comprende la intención antes de elegir colores:
- Audiencia — ¿quién ve esto? ¿Desarrolladores? ¿Ejecutivos? ¿Consumidores generales?
- Plataforma — ¿dónde se reproduce? ¿Redes sociales (15s), hero de sitio web, demo de producto, interno?
- Prioridad — ¿qué importa más? ¿Calidad del movimiento? ¿Precisión del contenido? ¿Fidelidad de la marca? ¿Velocidad?
- Variaciones — ¿el usuario quiere opciones o una única mejor toma?
Para solicitudes específicas ("añade una tarjeta de título", "arregla el tiempo en la escena 3"), salta el descubrimiento.
Para solicitudes exploratorias, considera ofrecer 2-3 variaciones que difieran significativamente — no solo cambios de color, sino diferentes ritmos, niveles de energía o enfoques estructurales. Una segura/esperada, una ambiciosa. No lo hagas obligatorio: es una herramienta disponible cuando sea apropiado.
Paso 1: Sistema de diseño
Si existe design.md o DESIGN.md en el proyecto, léelo primero (verifica ambas mayúsculas/minúsculas — son archivos diferentes en Linux). Es la fuente de verdad para los colores de la marca, las fuentes y las restricciones. Usa sus valores exactos — no inventes colores ni sustituyas fuentes. Cualquier formato funciona (frontmatter YAML, prosa, tablas — solo extrae los valores).
Si nombra fuentes que no puedes encontrar localmente (sin directorio fonts/ con archivos .woff2, no es una fuente incorporada), advierte al usuario antes de escribir HTML: "design.md especifica [nombre de la fuente] pero no se encontraron archivos de fuente. Agrega archivos .woff2 a fonts/ o recurriré a la alternativa incorporada más cercana [nombre de la alternativa]."
Si no existe design.md, ofrece al usuario una opción:
- ¿El usuario nombró un estilo o ambiente? → Lee visual-styles.md para los 8 ajustes preestablecidos con nombre. Elige la coincidencia más cercana.
- ¿Quiere explorar opciones visualmente? → Ejecuta el selector de diseño: lee references/design-picker.md para el flujo de trabajo completo. Esto sirve una página de selector visual. El usuario configura el ambiente, la paleta, la tipografía y el movimiento en el navegador, luego copia el design.md generado y lo pega de nuevo en la conversación.
- ¿Quiere saltar esto e ir rápido? → Pregunta: ambiente, claro u oscuro, ¿colores/fuentes de marca? Luego elige una paleta de house-style.md.
design.md define la marca. No define las reglas de composición de video. Esas provienen de references/video-composition.md y house-style.md. Usa los colores de la marca a una escala apropiada para video, no a la opacidad de una interfaz web.
Paso 2: Expansión del prompt
Siempre se ejecuta en cada composición (excepto piezas de una sola escena y ediciones triviales). Este paso fundamenta la intención del usuario contra design.md y house-style.md y produce un intermedio consistente que todos los agentes posteriores leen de la misma manera.
Lee references/prompt-expansion.md para el proceso completo y el formato de salida.
Paso 3: Planificar
Antes de escribir HTML, piensa a alto nivel:
- Qué — ¿qué debería experimentar el espectador? Identifica el arco narrativo, los momentos clave y los latidos emocionales.
- Estructura — ¿cuántas composiciones, cuáles son subcomposiciones vs. en línea, qué pistas llevan qué (video, audio, superposiciones, subtítulos)?
- Ritmo — declara el ritmo de la escena antes de implementar. Qué escenas son golpes rápidos, cuáles son pausas, dónde aterrizan los shaders, dónde alcanza su punto máximo la energía. Nombra el patrón: rápido-rápido-LENTO-rápido-SHADER-pausa. Lee references/beat-direction.md para plantillas de ritmo.
- Tiempo — qué clips impulsan la duración, dónde aterrizan las transiciones, cuál es el ritmo.
- Diseño — construye primero el estado final. Consulta "Diseño antes de la animación" a continuación.
- Animar — luego agrega movimiento usando las reglas a continuación.
Construye lo que se pidió. Una solicitud de "una tarjeta de título" no es una solicitud de "una tarjeta de título + 3 escenas de apoyo + música ambiental + subtítulos". Cada escena, cada elemento, cada tween debe ganarse su lugar. Si escenas o elementos adicionales mejoraran genuinamente la pieza, propónlos — no los agregues.
Para ediciones pequeñas (corregir un color, ajustar el tiempo, agregar un elemento), salta directamente a las reglas.
Diseño antes de la animación
Posiciona cada elemento donde debería estar en su momento más visible — el fotograma en el que ha entrado completamente, está colocado correctamente y aún no está saliendo. Escribe esto como HTML+CSS estático primero. Aún no hay GSAP.
Por qué esto importa: Si posicionas los elementos en su estado inicial animado (fuera de pantalla, escalados a 0, opacidad 0) y los interpolas hacia donde crees que deberían aterrizar, estás adivinando el diseño final. Las superposiciones son invisibles hasta que se renderiza el video. Al construir el estado final primero, puedes ver y solucionar problemas de diseño antes de agregar cualquier movimiento.
El proceso
- Identifica el fotograma héroe para cada escena — el momento en que la mayoría de los elementos son visibles simultáneamente. Este es el diseño que construyes.
- Escribe CSS estático para ese fotograma. El contenedor
.scene-contentDEBE llenar la escena completa usandowidth: 100%; height: 100%; padding: Npx;condisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Usa padding para empujar el contenido hacia adentro — NUNCA usesposition: absolute; top: Npxen un contenedor de contenido. Los contenedores de contenido posicionados con absolute se desbordan cuando el contenido es más alto que el espacio restante. Reservaposition: absolutesolo para elementos decorativos. - Agrega entradas con
gsap.from()— anima DESDE fuera de pantalla/invisible HASTA la posición CSS. La posición CSS es la verdad fundamental; el tween describe el viaje para llegar allí. (En subcomposiciones cargadas a través dedata-composition-src, prefieregsap.fromTo()— consulta las reglas GSAP de carga en references/motion-principles.md.) - Agrega salidas con
gsap.to()— anima HASTA fuera de pantalla/invisible DESDE la posición CSS.
Ejemplo
/* scene-content llena la escena, el padding posiciona el contenido */
.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;
}
/* El contenedor llena cualquier tamaño de escena (1920x1080, 1080x1920, etc.).
El padding posiciona el contenido. Flex + gap maneja el espaciado. */
INCORRECTO — dimensiones fijas y posicionamiento absoluto:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Paso 3: Animar HACIA esas posiciones
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);
// Paso 4: Animar FUERA de esas posiciones
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);
Cuando los elementos comparten espacio a lo largo del tiempo
Si el elemento A sale antes de que el elemento B entre en la misma área, ambos deben tener posiciones CSS correctas para sus respectivos fotogramas héroes. El orden de la línea de tiempo garantiza que nunca coexistan visualmente, pero si omites el paso de diseño, no detectarás el caso en que se superpongan accidentalmente debido a un error de tiempo.
Qué cuenta como superposición intencional
Los efectos en capas (brillo detrás del texto, elementos de sombra, patrones de fondo) y los diseños apilados en z (pilas de tarjetas, capas de profundidad) son intencionales. El paso de diseño se trata de detectar superposiciones no intencionales: dos titulares cayendo uno sobre otro, una estadística cubriendo una etiqueta, contenido que se sale del fotograma.
Atributos de datos
Todos los clips
| Atributo | Requerido | Valores |
|---|---|---|
id | Sí | Identificador único |
data-start | Sí | Segundos o referencia de ID de clip ("el-1", "intro + 2") |
data-duration | Requerido para img/div/composiciones | Segundos. Video/audio usa duración del medio por defecto. |
data-track-index | Sí | Entero. Los clips de la misma pista no pueden superponerse. |
data-media-start | No | Desplazamiento de recorte en la fuente (segundos) |
data-volume | No | 0-1 (por defecto 1) |
data-track-index no afecta la superposición visual — usa CSS z-index.
Clips de composición
| Atributo | Requerido | Valores |
|---|---|---|
data-composition-id | Sí | ID de composición único |
data-start | Sí | Hora de inicio (composición raíz: usa "0") |
data-duration | Sí | Tiene prioridad sobre la duración de la línea de tiempo GSAP |
data-width / data-height | Sí | Dimensiones en píxeles (1920x1080 o 1080x1920) |
data-composition-src | No | Ruta a un archivo HTML externo |
data-variable-values | No | Objeto JSON de anulaciones de variables por instancia en un host de subcomp |
En el elemento raíz <html>:
| Atributo | Requerido | Valores |
|---|---|---|
data-composition-variables | No | Array JSON de variables declaradas (id/tipo/etiqueta/defecto): impulsa la UI de edición de Studio y proporciona defectos para getVariables() |
Estructura de la composición
Las subcomposiciones cargadas a través de data-composition-src usan un envoltorio <template>. Las composiciones independientes (el index.html principal) NO usan <template> — colocan el div data-composition-id directamente en <body>. Usar <template> en un archivo independiente oculta todo el contenido del navegador y rompe el renderizado.
Estructura de subcomposición:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- contenido -->
<style>
[data-composition-id="my-comp"] {
/* estilos con ámbito */
}
</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>
Cargar en raíz: <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>
Variables (Composiciones parametrizadas)
Renderiza la misma composición con contenido diferente — título, color de tema, precios, subtítulos — sin editar el HTML fuente.
Patrón de tres pasos:
- Declara variables en la raíz
<html>de la composición condata-composition-variables. Cada entrada necesitaid,type(uno destring,number,color,boolean,enum),labelydefault. Las entradas de tipo enum también necesitanoptions: [{value, label}, ...]. - Lee los valores resueltos dentro del script de la composición con
window.__hyperframes.getVariables(). Devuelve el resultado fusionado de los defectos declarados + anulaciones por instancia + anulaciones CLI. - Anula en tiempo de renderizado con
npx hyperframes render --variables '{...}'(nivel superior) o condata-variable-values='{...}'en el elemento host (por instancia para subcomposiciones).
<!doctype html>
<html
data-composition-variables='[
{"id":"title","type":"string","label":"Título","default":"Hola"},
{"id":"theme","type":"enum","label":"Tema","default":"light","options":[
{"value":"light","label":"Claro"},
{"value":"dark","label":"Oscuro"}
]}
]'
>
<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>
# La vista previa de desarrollo usa los defectos declarados
npx hyperframes preview
# Renderizar con anulaciones
npx hyperframes render --variables '{"title":"Informe Q4","theme":"dark"}' --output q4.mp4
# O desde un archivo JSON
npx hyperframes render --variables-file ./vars.json
Valores por instancia de subcomposición: el mismo getVariables() funciona dentro de subcomposiciones cargadas a través de data-composition-src. Cada elemento host pasa sus propios 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 traducción]