Communitygithub.com

Parcha-ai/build

Crea composiciones de video, animaciones, títulos, superposiciones, subtítulos, voces en off, visuales reactivos al audio y transiciones de escena en HyperFrames HTML.

¿Qué es build?

Esta habilidad permite a los agentes de IA producir contenido de video profesional directamente desde HTML, utilizando el framework HyperFrames. Sirve como guía completa para la creación de composiciones de video, incluyendo la estructura de escenas, animación con GSAP, manejo de sincronización de clips de video/audio, y transiciones entre escenas. Es especialmente útil para desarrolladores que desean generar videos programáticamente, evitando la edición manual, y soporta cualquier agente que entienda HyperFrames HTML (como Claude Code o Cursor). La habilidad enfatiza un enfoque determinista y estructurado: primero se define el layout final (el 'hero frame' de cada escena) usando CSS estático, y luego se añaden animaciones de entrada con `gsap.from()` y salidas con `gsap.to()`, siempre respetando reglas estrictas como la prohibición de `repeat: -1`, animaciones asíncronas, o superposiciones accidentales. También cubre la carga de sub-composiciones externas, sincronización de pistas de audio/video, y verificaciones de calidad como contraste WCAG.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Parcha-ai/build/tree/HEAD/.agents/skills/hyperframes

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Vista previa

Del README del skill

Documentación

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 de medios y la sincronización de la línea de tiempo.

Enfoque

Antes de escribir HTML, piensa a alto nivel:

  1. Qué — qué debería experimentar el espectador? Identifica el arco narrativo, los momentos clave y los latidos emocionales.
  2. Estructura — cuántas composiciones, cuáles son sub-composiciones vs. en línea, qué pistas llevan qué (video, audio, superposiciones, subtítulos).
  3. Tiempo — qué clips determinan la duración, dónde caen las transiciones, cuál es el ritmo.
  4. Diseño — construye el estado final primero. Ver "Diseño Antes de la Animación" abajo.
  5. Animar — luego añade movimiento usando las reglas de abajo.

Para ediciones pequeñas (corregir un color, ajustar el tiempo, añadir un elemento), salta directamente a las reglas.

Cuando no se proporcione un visual-style.md o dirección de animación, sigue house-style.md para valores predeterminados de movimiento, tamaño y paletas de colores.

Diseño Antes de la Animación

Posiciona cada elemento donde debería estar en su momento más visible — el fotograma donde ha entrado completamente, está colocado correctamente y aún no está saliendo. Escribe esto como HTML+CSS estático primero. Sin GSAP todavía.

Por qué es importante: Si posicionas elementos en su estado inicial animado (fuera de pantalla, escalados a 0, opacidad 0) y los interpolas a donde crees que deberían aterrizar, estás adivinando el diseño final. Las superposiciones son invisibles hasta que el video se renderiza. Al construir el estado final primero, puedes ver y solucionar problemas de diseño antes de añadir cualquier movimiento.

El proceso

  1. Identifica el fotograma héroe para cada escena — el momento en que la mayoría de los elementos son visibles simultáneamente. Ese es el diseño que construyes.
  2. Escribe CSS estático para ese fotograma. El contenedor .scene-content DEBE llenar toda la escena usando width: 100%; height: 100%; padding: Npx; con display: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Usa padding para empujar el contenido hacia adentro — NUNCA uses position: absolute; top: Npx en un contenedor de contenido. Los contenedores de contenido con posición absoluta se desbordan cuando el contenido es más alto que el espacio restante. Reserva position: absolute solo para elementos decorativos.
  3. Añade entradas con gsap.from() — anima DESDE fuera de pantalla/invisible HASTA la posición CSS. La posición CSS es la verdad absoluta; la interpolación describe el viaje para llegar allí.
  4. Añade 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 codificadas 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 desde 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 te saltas 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 consiste en detectar superposiciones no intencionales — dos titulares cayendo uno encima del otro, una estadística cubriendo una etiqueta, contenido saliéndose del fotograma.

Atributos de Datos

Todos los Clips

AtributoRequeridoValores
idSíIdentificador único
data-startSíSegundos o referencia de ID de clip ("el-1", "intro + 2")
data-durationRequerido para img/div/composicionesSegundos. Video/audio por defecto usa la duración del medio.
data-track-indexSíEntero. Los clips en la misma pista no pueden superponerse.
data-media-startNoDesplazamiento de recorte en el origen (segundos)
data-volumeNo0-1 (por defecto 1)

data-track-index no afecta la superposición visual — usa CSS z-index.

Clips de Composición

AtributoRequeridoValores
data-composition-idSíID de composición único
data-startSíTiempo de inicio (composición raíz: usa "0")
data-durationSíTiene prioridad sobre la duración de la línea de tiempo GSAP
data-width / data-heightSíDimensiones en píxeles (1920x1080 o 1080x1920)
data-composition-srcNoRuta a un archivo HTML externo

Estructura de Composición

Las sub-composiciones cargadas a través de data-composition-src usan un contenedor <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 sub-composició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 });
      // interpolaciones...
      window.__timelines["my-comp"] = tl;
    </script>
  </div>
</template>

Cargar en la 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>

Video y Audio

El video debe tener muted playsinline. El audio siempre es un 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 de Línea de Tiempo

  • Todas las líneas de tiempo comienzan { paused: true } — el reproductor controla la reproducción
  • Registra cada línea de tiempo: window.__timelines["<composition-id>"] = tl
  • El framework anida automáticamente sub-líneas de tiempo — NO las agregues manualmente
  • La duración proviene de data-duration, no de la longitud de la línea de tiempo GSAP
  • Nunca crees interpolaciones vacías para establecer la duración

Reglas (No Negociables)

Determinista: Sin Math.random(), Date.now(), o lógica basada en tiempo. Usa un PRNG con semilla si necesitas valores pseudoaleatorios (por ejemplo, mulberry32).

GSAP: Solo anima propiedades visuales (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, transformaciones). NO animes visibility, display, ni llames a video.play()/audio.play().

Conflictos de animación: Nunca animes la misma propiedad en el mismo elemento desde múltiples líneas de tiempo simultáneamente.

Sin repeat: -1: Las líneas de tiempo con repetición infinita rompen el motor de captura. Calcula el número exacto de repeticiones a partir de la duración de la composición: repeat: Math.ceil(duration / cycleDuration) - 1.

Construcción síncrona de líneas de tiempo: Nunca construyas líneas de tiempo dentro de async/await, setTimeout, o Promesas. El motor de captura lee window.__timelines de forma síncrona después de la carga de la página. Las fuentes son incrustadas por el compilador, por lo que están disponibles inmediatamente — no es necesario esperar a que se carguen las fuentes.

Nunca hagas:

  1. Olvidar el registro de window.__timelines
  2. Usar video para audio — siempre video silenciado + <audio> separado
  3. Anidar video dentro de un div con tiempo — usa un contenedor sin tiempo
  4. Usar data-layer (usa data-track-index) o data-end (usa data-duration)
  5. Animar las dimensiones del elemento de video — anima un contenedor
  6. Llamar a play/pause/seek en medios — el framework posee la reproducción
  7. Crear un contenedor de nivel superior sin data-composition-id
  8. Usar repeat: -1 en cualquier línea de tiempo o interpolación — siempre repeticiones finitas
  9. Construir líneas de tiempo de forma asíncrona (dentro de async, setTimeout, Promise)
  10. Usar gsap.set() en elementos de clip de escenas posteriores — no existen en el DOM al cargar la página. Usa tl.set(selector, vars, timePosition) dentro de la línea de tiempo en o después del tiempo data-start del clip.
  11. Usar <br> en el texto del contenido — los saltos de línea forzados no tienen en cuenta el ancho real de la fuente renderizada. El texto que se ajusta naturalmente + un <br> produce un salto extra no deseado, causando superposición. Deja que el texto se ajuste usando max-width. Excepción: títulos cortos de visualización donde cada palabra está deliberadamente en su propia línea (por ejemplo, "EL\nJUEGO\nINMORTAL" a 130px).

Transiciones de Escena (No Negociables)

Toda composición de múltiples escenas DEBE seguir TODAS estas reglas. Violar cualquiera de ellas es una composición rota.

  1. SIEMPRE usa transiciones entre escenas. Sin cortes abruptos. Sin excepciones.
  2. SIEMPRE usa animaciones de entrada en cada escena. Cada elemento anima ENTRANDO a través de gsap.from(). Ningún elemento puede aparecer completamente formado. Si una escena tiene 5 elementos, necesita 5 interpolaciones de entrada.
  3. NUNCA uses animaciones de salida excepto en la escena final. Esto significa: NO gsap.to() que anime opacidad a 0, y fuera de pantalla, escala a 0, o cualquier otra animación de "salida" antes de que se active una transición. La transición ES la salida. El contenido de la escena saliente DEBE ser completamente visible en el momento en que comienza la transición.
  4. Solo escena final: La última escena puede desvanecer elementos (por ejemplo, fundido a negro). Esta es la ÚNICA escena donde se permite gsap.to(..., { opacity: 0 }).

INCORRECTO — animación de salida antes de la transición:

// PROHIBIDO — esto vacía la escena antes de que la transición pueda usarla
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// la transición se activa en un fotograma vacío

CORRECTO — solo entrada, la transición maneja la salida:

// Animaciones de entrada de la escena 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);
// Sin interpolaciones de salida — la transición a los 7.2s maneja el cambio de escena
// Animaciones de entrada de la escena 2
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);

Barreras de Protección de Animación

  • Desplaza la primera animación 0.1-0.3s (no en t=0)
  • Varía las curvas de easing entre las interpolaciones de entrada — usa al menos 3 curvas de easing diferentes por escena
  • No repitas un patrón de entrada dentro de una escena
  • Evita gradientes lineales de pantalla completa en fondos oscuros (banding H.264 — usa radial o sólido + brillo localizado)
  • Titulares de 60px+, cuerpo de 20px+, etiquetas de datos de 16px+ para video renderizado
  • font-variant-numeric: tabular-nums en columnas de números

Cuando no se proporcione un visual-style.md o dirección de animación, sigue house-style.md para valores predeterminados estéticos.

Tipografía y Recursos

  • Fuentes: Simplemente escribe el font-family que quieras en CSS — el compilador incrusta las fuentes compatibles automáticamente. Si una fuente no es compatible, el compilador advierte.
  • Añade crossorigin="anonymous" a medios externos
  • Para desbordamiento de texto dinámico, usa window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })
  • Todos los archivos residen en la raíz del proyecto junto a index.html; las sub-composiciones usan ../

Editar Composiciones Existentes

  • Lee la composición completa primero — busca coincidir fuentes, colores y patrones de animación existentes
  • Solo cambia lo que se solicitó
  • Preserva el tiempo de los clips no relacionados

Lista de Verificación de Salida

  • npx hyperframes lint y npx hyperframes validate pasan ambos
  • Las advertencias de contraste fueron abordadas (ver Verificaciones de Calidad abajo)
  • Coreografía de animación verificada (ver Verificaciones de Calidad abajo)

Verificaciones de Calidad

Contraste

hyperframes validate ejecuta una auditoría de contraste WCAG de forma predeterminada. Busca 5 marcas de tiempo, captura la página, muestra píxeles de fondo detrás de cada elemento de texto y calcula las relaciones de contraste. Los fallos aparecen como advertencias:

⚠ Advertencias de contraste WCAG AA (3):
  · .subtitle "texto secundario" — 2.67:1 (necesita 4.5:1, t=5.3s)

Si aparecen advertencias:

  • En fondos oscuros: aclara el color fallido hasta que supere 4.5:1 (texto normal) o 3:1 (texto grande, 24px+ o 19px+ en negrita)
  • En fondos claros: oscurécelo
  • Mantente dentro de la familia de la paleta — no inventes un nuevo color, ajusta el existente
  • Vuelve a ejecutar hyperframes validate hasta que esté limpio

Usa --no-contrast para saltar si iteras rápidamente y lo verificarás después.

Mapa de Animación

Después de crear las animaciones, ejecuta el mapa de animación para verificar la coreografía:

Skills relacionados