HyperFrames
HTML ist die Quelle der Wahrheit für Video. Eine Komposition ist eine HTML-Datei mit data-*-Attributen für das Timing, einer GSAP-Timeline für Animationen und CSS für das Erscheinungsbild. Das Framework kümmert sich um Clip-Sichtbarkeit, Medienwiedergabe und Timeline-Synchronisation.
Ansatz
Discovery (nur für explorative Anfragen)
Bei offenen Anfragen („Erstelle mir ein Produkt-Launch-Video“, „Erstelle etwas für unsere Marke“), bei denen der Nutzer sich noch nicht auf eine Richtung festgelegt hat, verstehe die Absicht, bevor du Farben auswählst:
- Zielgruppe — Wer sieht sich das an? Entwickler? Führungskräfte? Allgemeine Verbraucher?
- Plattform — Wo wird es abgespielt? Social Media (15s), Website-Hero, Produkt-Demo, intern?
- Priorität — Was ist am wichtigsten? Bewegungsqualität? Inhaltsgenauigkeit? Markentreue? Geschwindigkeit?
- Varianten — Möchte der Nutzer Optionen oder einen einzigen, besten Versuch?
Bei spezifischen Anfragen („Füge eine Titelcard hinzu“, „Korrigiere das Timing in Szene 3“) überspringe die Discovery.
Bei explorativen Anfragen solltest du in Betracht ziehen, 2–3 Variationen anzubieten, die sich sinnvoll unterscheiden – nicht nur Farbwechsel, sondern unterschiedliches Tempo, Energieniveaus oder strukturelle Ansätze. Eine sichere/erwartete, eine ambitionierte. Schreibe dies nicht vor – es ist ein Werkzeug, das bei Bedarf eingesetzt werden kann.
Schritt 1: Designsystem
Wenn design.md oder DESIGN.md im Projekt existiert, lies es zuerst (überprüfe beide Schreibweisen – auf Linux sind es unterschiedliche Dateien). Es ist die Quelle der Wahrheit für Markenfarben, Schriftarten und Einschränkungen. Verwende seine exakten Werte – erfinde keine Farben oder ersetze Schriftarten. Jedes Format ist in Ordnung (YAML-Frontmatter, Prosa, Tabellen – extrahiere einfach die Werte).
Wenn es Schriftarten nennt, die du lokal nicht finden kannst (kein fonts/-Verzeichnis mit .woff2-Dateien, keine integrierte Schriftart), warne den Nutzer, bevor du HTML schreibst: „design.md spezifiziert [Schriftartname], aber es wurden keine Schriftartdateien gefunden. Bitte füge .woff2-Dateien zu fonts/ hinzu, oder ich werde auf [nächstgelegene integrierte Alternative] zurückfallen.“
Wenn keine design.md existiert, biete dem Nutzer eine Auswahl an:
- Hat der Nutzer einen Stil oder eine Stimmung genannt? → Lese visual-styles.md für die 8 benannten Voreinstellungen. Wähle die nächste Übereinstimmung.
- Möchte der Nutzer Optionen visuell durchstöbern? → Führe den Design-Picker aus: Lese references/design-picker.md für den vollständigen Workflow. Dies dient einer visuellen Auswahlseite. Der Nutzer konfiguriert Stimmung, Palette, Typografie und Bewegung im Browser, kopiert dann das generierte design.md und fügt es zurück in die Konversation.
- Möchte der Nutzer es überspringen und schnell vorankommen? → Frage: Stimmung, hell oder dunkel, irgendwelche Markenfarben/Schriftarten? Wähle dann eine Palette aus house-style.md.
design.md definiert die Marke. Es definiert nicht die Videokompositionsregeln. Diese kommen aus references/video-composition.md und house-style.md. Verwende Markenfarben in videoangemessener Skalierung – nicht in Web-UI-Transparenz.
Schritt 2: Prompt-Erweiterung
Führe dies immer bei jeder Komposition durch (außer bei Einzelszenen-Stücken und trivialen Bearbeitungen). Dieser Schritt verankert die Absicht des Nutzers in Bezug auf design.md und house-style.md und erzeugt ein konsistentes Zwischenergebnis, das jeder nachgelagerte Agent auf die gleiche Weise liest.
Lese references/prompt-expansion.md für den vollständigen Prozess und das Ausgabeformat.
Schritt 3: Plan
Bevor du HTML schreibst, denke auf hohem Niveau nach:
- Was — Was soll der Betrachter erleben? Identifiziere den narrativen Bogen, Schlüsselmomente und emotionale Beats.
- Struktur — Wie viele Kompositionen, welche sind Unterkompositionen vs. inline, welche Spuren tragen was (Video, Audio, Overlays, Untertitel).
- Rhythmus — Gib deinen Szenenrhythmus an, bevor du implementierst. Welche Szenen sind schnelle Treffer, welche sind Haltepunkte, wo landen Shader, wo ist der Energiehöhepunkt. Benenne das Muster: schnell-schnell-LANGSAM-schnell-SHADER-halten. Lese references/beat-direction.md für Rhythmus-Vorlagen.
- Timing — Welche Clips bestimmen die Dauer, wo landen Übergänge, wie ist das Tempo.
- Layout — Baue zuerst den Endzustand. Siehe „Layout vor Animation“ unten.
- Animieren — Füge dann Bewegung mit den Regeln unten hinzu.
Baue, worum gebeten wurde. Eine Anfrage nach „einer Titelcard“ ist keine Anfrage nach „einer Titelcard + 3 unterstützenden Szenen + Hintergrundmusik + Untertiteln.“ Jede Szene, jedes Element, jedes Tween sollte seinen Platz verdienen. Wenn zusätzliche Szenen oder Elemente das Stück tatsächlich verbessern würden, schlage sie vor – füge sie nicht einfach hinzu.
Bei kleinen Bearbeitungen (eine Farbe korrigieren, Timing anpassen, ein Element hinzufügen) gehe direkt zu den Regeln.
Layout vor Animation
Positioniere jedes Element dort, wo es in seinem sichtbarsten Moment sein soll – dem Frame, in dem es vollständig eingetreten, korrekt platziert und noch nicht am Verlassen ist. Schreibe dies zuerst als statisches HTML+CSS. Noch kein GSAP.
Warum das wichtig ist: Wenn du Elemente in ihrem animierten Startzustand positionierst (außerhalb des Bildschirms, skaliert auf 0, Deckkraft 0) und sie dahin bewegst, wo du denkst, dass sie landen sollen, rätst du das endgültige Layout. Überlappungen sind unsichtbar, bis das Video gerendert wird. Indem du zuerst den Endzustand baust, kannst du Layoutprobleme sehen und beheben, bevor du Bewegung hinzufügst.
Der Prozess
- Identifiziere den Helden-Frame für jede Szene – den Moment, in dem die meisten Elemente gleichzeitig sichtbar sind. Dies ist das Layout, das du baust.
- Schreibe statisches CSS für diesen Frame. Der
.scene-content-Container MUSS die gesamte Szene ausfüllen, indem erwidth: 100%; height: 100%; padding: Npx;mitdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-boxverwendet. Verwende Padding, um Inhalt nach innen zu drücken – NIEMALSposition: absolute; top: Npxauf einem Inhaltscontainer. Absolut positionierte Inhaltscontainer laufen über, wenn der Inhalt höher ist als der verbleibende Platz. Reserviereposition: absolutenur für dekorative Elemente. - Füge Eingänge mit
gsap.from()hinzu – animiere VON außerhalb des Bildschirms/unsichtbar ZUM CSS-Zustand. Der CSS-Zustand ist die Grundwahrheit; das Tween beschreibt die Reise dorthin. (In Unterkompositionen, die überdata-composition-srcgeladen werden, bevorzugegsap.fromTo()– siehe tragende GSAP-Regeln in references/motion-principles.md.) - Füge Ausgänge mit
gsap.to()hinzu – animiere ZU außerhalb des Bildschirms/unsichtbar VOM CSS-Zustand.
Beispiel
/* scene-content füllt die Szene, Padding positioniert den Inhalt */
.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;
}
/* Container füllt jede Szenengröße (1920x1080, 1080x1920, etc.).
Padding positioniert den Inhalt. Flex + Gap handhabt Abstände. */
FALSCH – fest codierte Abmessungen und absolute Positionierung:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Schritt 3: Animieren IN diese Positionen
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);
// Schritt 4: Animieren AUS diesen Positionen
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);
Wenn Elemente sich Platz über die Zeit teilen
Wenn Element A ausgeht, bevor Element B im selben Bereich eintritt, sollten beide korrekte CSS-Positionen für ihre jeweiligen Helden-Frames haben. Die Timeline-Reihenfolge garantiert, dass sie nie visuell nebeneinander existieren – aber wenn du den Layout-Schritt überspringst, wirst du den Fall nicht abfangen, in dem sie sich aufgrund eines Timing-Fehlers versehentlich überlappen.
Was als beabsichtigte Überlappung gilt
Geschichtete Effekte (Glühen hinter Text, Schattenelemente, Hintergrundmuster) und Z-geschichtete Designs (Kartenstapel, Tiefenschichten) sind beabsichtigt. Der Layout-Schritt dient dazu, unbeabsichtigte Überlappungen zu erkennen – zwei Überschriften, die übereinander landen, eine Statistik, die ein Label bedeckt, Inhalt, der aus dem Frame ausblutet.
Datenattribute
Alle Clips
| Attribut | Erforderlich | Werte |
|---|---|---|
id | Ja | Eindeutige Kennung |
data-start | Ja | Sekunden oder Clip-ID-Referenz ("el-1", "intro + 2") |
data-duration | Erforderlich für img/div/Kompositionen | Sekunden. Video/Audio standardmäßig auf Mediendauer. |
data-track-index | Ja | Ganze Zahl. Clips auf derselben Spur können sich nicht überlappen. |
data-media-start | Nein | Offset zum Trimmen in der Quelle (Sekunden) |
data-volume | Nein | 0-1 (Standard 1) |
data-track-index hat keinen Einfluss auf die visuelle Schichtung – verwende CSS z-index.
Kompositions-Clips
| Attribut | Erforderlich | Werte |
|---|---|---|
data-composition-id | Ja | Eindeutige Kompositions-ID |
data-start | Ja | Startzeit (Root-Komposition: verwende "0") |
data-duration | Ja | Hat Vorrang vor der GSAP-Timeline-Dauer |
data-width / data-height | Ja | Pixelabmessungen (1920x1080 oder 1080x1920) |
data-composition-src | Nein | Pfad zu einer externen HTML-Datei |
data-variable-values | Nein | JSON-Objekt mit instanzspezifischen Variablenüberschreibungen auf einem Sub-Comp-Host |
Auf dem Root-<html>-Element:
| Attribut | Erforderlich | Werte |
|---|---|---|
data-composition-variables | Nein | JSON-Array deklarierter Variablen (id/type/label/default) – steuert die Studio-Bearbeitungs-UI und stellt Standardwerte für getVariables() bereit |
Kompositionsstruktur
Unterkompositionen, die über data-composition-src geladen werden, verwenden einen <template>-Wrapper. Eigenständige Kompositionen (die Haupt-index.html) verwenden KEINEN <template> – sie platzieren das data-composition-id-Div direkt im <body>. Die Verwendung von <template> in einer eigenständigen Datei verbirgt alle Inhalte vor dem Browser und unterbricht das Rendering.
Struktur einer Unterkomposition:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- Inhalt -->
<style>
[data-composition-id="my-comp"] {
/* Gültige Styles */
}
</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>
Laden im Root: <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>
Variablen (parametrisierte Kompositionen)
Rendere dieselbe Komposition mit unterschiedlichem Inhalt – Titel, Theme-Farbe, Preise, Untertitel – ohne die Quell-HTML zu bearbeiten.
Drei-Schritt-Muster:
- Deklariere Variablen auf dem Root-
<html>der Komposition mitdata-composition-variables. Jeder Eintrag benötigtid,type(einer vonstring,number,color,boolean,enum),labelunddefault. Enum-Einträge benötigen auchoptions: [{value, label}, ...]. - Lese die aufgelösten Werte innerhalb des Skripts der Komposition mit
window.__hyperframes.getVariables(). Gibt das zusammengeführte Ergebnis von deklarierten Standardwerten + instanzspezifischen Überschreibungen + CLI-Überschreibungen zurück. - Überschreibe zur Renderzeit mit
npx hyperframes render --variables '{...}'(oberste Ebene) oder mitdata-variable-values='{...}'auf dem Host-Element (instanzspezifisch für Sub-Comps).
<!doctype html>
<html
data-composition-variables='[
{"id":"title","type":"string","label":"Titel","default":"Hallo"},
{"id":"theme","type":"enum","label":"Theme","default":"light","options":[
{"value":"light","label":"Hell"},
{"value":"dark","label":"Dunkel"}
]}
]'
>
<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>
# Entwicklungsvorschau verwendet deklarierte Standardwerte
npx hyperframes preview
# Rendern mit Überschreibungen
npx hyperframes render --variables '{"title":"Q4-Bericht","theme":"dark"}' --output q4.mp4
# Oder aus einer JSON-Datei
npx hyperframes render --variables-file ./vars.json
Instanzspezifische Werte für Sub-Kompositionen: Dieselbe getVariables() funktioniert innerhalb von Sub-Comps, die über data-composition-src geladen werden. Jedes Host-Element übergibt seine eigenen Werte:
<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"
data-variable-values='{"title":"Enterprise","price":"99€"}'
></div>