HyperFrames
HTML はビデオの唯一のソースオブトゥルースです。コンポジションは、タイミングのための data-* 属性、アニメーションのための GSAP タイムライン、そして外観のための CSS を持つ HTML ファイルです。フレームワークは、クリップの可視性、メディア再生、タイムライン同期を処理します。
アプローチ
ディスカバリー(探索的なリクエストのみ)
ユーザーが方向性を決めていないオープンエンドなリクエスト(「製品ローンチビデオを作って」「ブランド用に何か作って」など)では、色を決める前に意図を理解します:
- 視聴者 — 誰が見るのか?開発者?経営幹部?一般消費者?
- プラットフォーム — どこで再生されるのか?ソーシャルメディア(15秒)、Webサイトのヒーロー、製品デモ、社内向け?
- 優先事項 — 何が最も重要か?モーションの品質?コンテンツの正確性?ブランドの忠実度?速度?
- バリエーション — ユーザーは複数の選択肢を望んでいるのか、それとも単一のベストな案を望んでいるのか?
具体的なリクエスト(「タイトルカードを追加して」「シーン3のタイミングを修正して」)の場合は、ディスカバリーをスキップします。
探索的なリクエストについては、意味のある違いがある2〜3のバリエーションを検討します — 単なる色の変更ではなく、異なるペーシング、エネルギーレベル、または構造的なアプローチです。一つは安全で予想されるもの、もう一つは野心的なもの。これを必須にはしません — 適切な場合に利用可能なツールです。
ステップ 1: デザインシステム
design.md または DESIGN.md がプロジェクトに存在する場合、まずそれを読みます(両方の大文字小文字を確認 — Linux では別のファイルです)。ブランドカラー、フォント、制約の唯一のソースオブトゥルースです。正確な値を使用し — 色を発明したりフォントを代替したりしないでください。どのような形式でも機能します(YAML フロントマター、散文、表 — 値を抽出するだけです)。
指定されたフォントがローカルに見つからない場合(fonts/ ディレクトリに .woff2 ファイルがなく、組み込みフォントでもない場合)、HTML を書く前にユーザーに警告します:「design.md に [フォント名] が指定されていますが、フォントファイルが見つかりません。fonts/ に .woff2 ファイルを追加してください。追加されない場合は、最も近い組み込みの代替フォントを使用します。」
design.md が存在しない場合は、ユーザーに選択肢を提示します:
- ユーザーがスタイルや雰囲気を指定した? → visual-styles.md を読み、8つの名前付きプリセットから最も近いものを選びます。
- 視覚的にオプションを閲覧したい? → デザインピッカーを実行:reference/design-picker.md を読み、完全なワークフローを確認します。これにより視覚的なピッカーページが提供されます。ユーザーはブラウザで雰囲気、パレット、タイポグラフィ、モーションを設定し、生成された design.md をコピーして会話に貼り付けます。
- スキップして迅速に進めたい? → 雰囲気、明るいか暗いか、ブランドカラーやフォントはあるか?を尋ねます。次に house-style.md からパレットを選びます。
design.md はブランドを定義します。ビデオコンポジションのルールを定義するものではありません。 それらのルールは references/video-composition.md と house-style.md から来ます。ブランドカラーはビデオに適したスケールで使用します — Web UI の不透明度ではありません。
ステップ 2: プロンプト拡張
すべてのコンポジション(単一シーンの作品と些細な編集を除く)で常に実行します。このステップは、ユーザーの意図を design.md と house-style.md に照らして具体化し、すべての下流エージェントが同じように読み取る一貫した中間出力を生成します。
完全なプロセスと出力形式については references/prompt-expansion.md を読んでください。
ステップ 3: 計画
HTML を書く前に、高いレベルで考えます:
- 何を — 視聴者は何を体験すべきか?ナラティブアーク、キーモーメント、感情的なビートを特定します。
- 構造 — コンポジションはいくつあるか、どのコンポジションがサブコンポジション vs インラインか、どのトラックが何を運ぶか(ビデオ、オーディオ、オーバーレイ、キャプション)。
- リズム — 実装前にシーンのリズムを宣言します。どのシーンがクイックヒットか、どのシーンがホールドか、シェーダーがどこに配置されるか、エネルギーがどこでピークに達するか。パターンに名前を付けます:fast-fast-SLOW-fast-SHADER-hold。references/beat-direction.md でリズムテンプレートを読んでください。
- タイミング — どのクリップが再生時間を決定するか、トランジションはどこに配置されるか、ペーシングはどうか。
- レイアウト — まず最終状態を構築します。以下の「アニメーションの前にレイアウト」を参照。
- アニメーション — 次に、以下のルールを使用してモーションを追加します。
要求されたものを構築します。 「タイトルカード」のリクエストは、「タイトルカード + 3つの補助シーン + 背景音楽 + キャプション」のリクエストではありません。すべてのシーン、すべての要素、すべてのトゥイーンは、その存在意義を正当化する必要があります。追加のシーンや要素が作品を真に向上させる場合は提案します — 勝手に追加しないでください。
小さな編集(色の修正、タイミングの調整、1つの要素の追加)の場合は、すぐにルールにスキップします。
アニメーションの前にレイアウト
すべての要素を、最も目立つ瞬間 — 完全に入場し、正しく配置され、まだ退場していないフレーム — に配置します。これをまず静的な HTML+CSS として記述します。まだ GSAP はありません。
なぜこれが重要なのか: 要素をアニメーションの開始状態(画面外、スケール0、不透明度0)に配置し、そこから着地すべき場所にトゥイーンすると、最終的なレイアウトを推測していることになります。重なりはビデオがレンダリングされるまで見えません。最初に最終状態を構築することで、モーションを追加する前にレイアウトの問題を確認して修正できます。
プロセス
- 各シーンのヒーローフレームを特定 — 最も多くの要素が同時に表示される瞬間。これが構築するレイアウトです。
- そのフレームの静的 CSS を記述。
.scene-contentコンテナは、width: 100%; height: 100%; padding: Npx;、display: flex; flex-direction: column; gap: Npx; box-sizing: border-boxを使用してシーン全体を埋める必要があります。パディングを使用してコンテンツを内側に押し込みます — コンテンツコンテナにposition: absolute; top: Npxを決して使用しないでください。絶対配置のコンテンツコンテナは、コンテンツが残りのスペースよりも高い場合にオーバーフローします。position: absoluteは装飾要素のみに予約します。 gsap.from()で入場を追加 — 画面外/非表示から CSS の位置へアニメーションします。CSS の位置が真実であり、トゥイーンはそこに到達するための経路を記述します。(data-composition-srcでロードされたサブコンポジションでは、gsap.fromTo()を優先します — references/motion-principles.md の負荷のかかる GSAP ルールを参照。)gsap.to()で退場を追加 — CSS の位置から画面外/非表示へアニメーションします。
例
/* scene-content はシーンを埋め、パディングがコンテンツを配置 */
.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;
}
/* コンテナは任意のシーンサイズ(1920x1080、1080x1920 など)にフィット */
/* パディングがコンテンツを配置。Flex + gap がスペーシングを処理 */
間違い — ハードコードされた寸法と絶対配置:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// ステップ 3: それらの位置にアニメーションで入場
三目並べから(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
三目並べから(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
三目並べから(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// ステップ 4: それらの位置からアニメーションで退場
三目並べへ(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
三目並べへ(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
三目並べへ(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
要素が時間を超えてスペースを共有する場合
要素 A が要素 B が同じエリアに入場する前に退場する場合、両方ともそれぞれのヒーローフレームに対して正しい CSS 位置を持つ必要があります。タイムラインの順序は、それらが視覚的に共存しないことを保証します — しかし、レイアウトステップをスキップすると、タイミングエラーによって偶然重なるケースを見逃します。
意図的な重なりと見なされるもの
レイヤー効果(テキストの背後にあるグロー、影の要素、背景パターン)と z スタックデザイン(カードスタック、奥行きレイヤー)は意図的です。レイアウトステップの目的は、意図しない重なりを捕捉することです — 2つの見出しが互いに重なる、統計がラベルを覆う、コンテンツがフレームからはみ出るなど。
データ属性
すべてのクリップ
| 属性 | 必須 | 値 |
|---|---|---|
id | はい | 一意の識別子 |
data-start | はい | 秒数またはクリップ ID 参照("el-1"、"intro + 2") |
data-duration | img/div/コンポジションに必須 | 秒数。ビデオ/オーディオはメディアの長さをデフォルトとする。 |
data-track-index | はい | 整数。同じトラックのクリップは重複できません。 |
data-media-start | いいえ | ソースへのトリムオフセット(秒) |
data-volume | いいえ | 0-1(デフォルト 1) |
data-track-index は視覚的なレイヤリングに影響しません — CSS z-index を使用します。
コンポジションクリップ
| 属性 | 必須 | 値 |
|---|---|---|
data-composition-id | はい | 一意のコンポジション ID |
data-start | はい | 開始時間(ルートコンポジション:"0" を使用) |
data-duration | はい | GSAP タイムラインの長さよりも優先される |
data-width / data-height | はい | ピクセル寸法(1920x1080 または 1080x1920) |
data-composition-src | いいえ | 外部 HTML ファイルへのパス |
data-variable-values | いいえ | サブコンポジションホスト上のインスタンスごとの変数オーバーライドの JSON オブジェクト |
ルート <html> 要素上:
| 属性 | 必須 | 値 |
|---|---|---|
data-composition-variables | いいえ | 宣言された変数(id/type/label/default)の JSON 配列 — Studio 編集 UI を駆動し、getVariables() のデフォルトを提供する |
コンポジション構造
data-composition-src でロードされるサブコンポジションは <template> ラッパーを使用します。スタンドアロンコンポジション(メインの index.html)は <template> を使用しません — data-composition-id の div を直接 <body> に配置します。スタンドアロンファイルで <template> を使用すると、すべてのコンテンツがブラウザから隠され、レンダリングが壊れます。
サブコンポジションの構造:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- コンテンツ -->
<style>
[data-composition-id="my-comp"] {
/* スコープ付きスタイル */
}
</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 });
// トゥイーン...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
ルートでのロード:<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>
変数(パラメータ化されたコンポジション)
異なるコンテンツ(タイトル、テーマカラー、価格、キャプション)で同じコンポジションを、ソース HTML を編集せずにレンダリングします。
3 ステップのパターン:
- 宣言:コンポジションの
<html>ルートにdata-composition-variablesで変数を宣言します。各エントリにはid、type(string、number、color、boolean、enumのいずれか)、label、defaultが必要です。Enum エントリにはoptions: [{value, label}, ...]も必要です。 - 読み取り:コンポジションのスクリプト内で
window.__hyperframes.getVariables()を使用して解決された値を読み取ります。宣言されたデフォルト + インスタンスごとのオーバーライド + CLI オーバーライドのマージ結果を返します。 - オーバーライド:レンダリング時に
npx hyperframes render --variables '{...}'(トップレベル)またはホスト要素のdata-variable-values='{...}'(サブコンポジションのインスタンスごと)でオーバーライドします。
<!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>
# 開発プレビューは宣言されたデフォルトを使用
npx hyperframes preview
# オーバーライドでレンダリング
npx hyperframes render --variables '{"title":"Q4 レポート","theme":"dark"}' --output q4.mp4
# または JSON ファイルから
npx hyperframes render --variables-file ./vars.json
サブコンポジションのインスタンスごとの値: data-composition-src でロードされたサブコンポジション内でも同じ getVariables() が機能します。各ホスト要素は独自の値を渡します:
<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":"お問い合わせください"}'
></div>
ビデオコンポジションのルール
完全なルールについては references/video-composition.md を読んでください。これらのルールは常に適用されます:
- テキストの最大ラップ幅は 50% 未満にしない — 特に縦型ビデオ(1080x1920)では、パディングと flex を使用してタイトルを狭めすぎないでください。
- 色には常に
hsl()を使用し、hsl()からアニメーションする — 16進数やrgb()は避け、hslトゥイーンは補間がうまくいきます。 - GSAP キーフレーム構文はサポートされていません — キーフレームは避け、個別のトゥイーンのチェーンのために
tl.to().to().to()を使用します。 clip-pathをinset()またはcircle()と共に使用して、トランジション/リビールを作成する。<style>で GSAP アウトライン宣言を設定しない — GSAP がそれらをトゥイーンします。data-durationは GSAP の時間よりも優先されます — コンポジションのdata-durationが設定されている場合、それがコンポジションの全長を決定します。GSAP タイムラインはdata-durationの範囲内で動作します。data-durationが GSAP の時間よりも長い場合、コンポジションは GSAP の終了後、終了まで最後のフレームに留まります。- パーセンテージベースの幅/高さはルートコンポジションでは機能しません — ルートコンポジションで 100% 幅/高さを必要とする div には
data-widthとdata-heightを使用します。 <img>クリップのobject-fitにガードレールを使用する —data-durationが設定された画像は、object-fit: coverが設定されていなければなりません。
補足ルール
display: noneは使用しない — アクティブでないクリップはフレームワークによってopacity: 0に設定されます(上書きしないでください)。- 各スクリプトタグは一意の ID を持つ。
- 画像は
<img>タグで、loading="eager"を設定して予熱する。 <style>または<script>タグにdata-*時間属性を設定しない — これらは非表示の要素です。<script>タグ内でdocument.querySelectorまたはgetElementByIdを使用する場合、コンテキストのdata-composition-idでスコープを絞る。<source>タグを持つ<video>要素は、<video>自体にdata-startとdata-durationを設定する。- サブコンポジションでは
gsap.from()よりもgsap.fromTo()を優先する — これにより、サブコンポジションのスクリプト実行時(DOM が準備できているが CSS の位置がまだ適用されていない可能性がある)に期待される動作が保証されます。 - サブコンポジションは常に「paused: true」タイムラインを使用して window.__timelines に登録する — サブコンポジションの開始/停止はフレームワークによって管理されます。
完全な詳細については video-composition.md を参照してください。