HyperFrames
HTML がビデオの信頼できる情報源です。コンポジションは、タイミングのための data-* 属性、アニメーションのための GSAP タイムライン、および外観のための CSS を持つ HTML ファイルです。フレームワークは、クリップの可視性、メディア再生、およびタイムライン同期を処理します。
アプローチ
ディスカバリー(探索的リクエストのみ)
ユーザーが方向性を決めていないオープンエンドなリクエスト(「製品紹介ビデオを作って」、「ブランド用に何か作成して」)の場合、色を選ぶ前に意図を理解します:
- オーディエンス — 誰が見るのか?開発者?経営幹部?一般消費者?
- プラットフォーム — どこで再生されるのか?ソーシャル(15秒)、ウェブサイトヒーロー、製品デモ、社内?
- 優先順位 — 最も重要なことは?モーションの品質?コンテンツの正確性?ブランドの忠実性?速度?
- バリエーション — ユーザーはオプションを望んでいるのか、それとも単一の最善のものを望んでいるのか?
特定のリクエスト(「タイトルカードを追加して」、「シーン3のタイミングを修正して」)の場合は、ディスカバリーをスキップします。
探索的リクエストの場合は、意味のある違いがある2〜3のバリエーションを提供することを検討します。単なる色の変更ではなく、異なるペース、エネルギーレベル、または構造的アプローチです。1つは安全/予想通り、1つは野心的です。これを必須にしないでください — 適切な場合に利用可能なツールです。
ステップ 1: デザインシステム
design.md または DESIGN.md がプロジェクトに存在する場合、最初にそれを読みます(両方の大文字小文字を確認してください — Linux では異なるファイルです)。ブランドカラー、フォント、および制約の信頼できる情報源です。その正確な値を使用してください — 色を発明したり、フォントを代用したりしないでください。どのような形式でも機能します(YAML フロントマター、散文、テーブル — 値を抽出するだけです)。
ローカルで見つけられないフォントが指定されている場合(fonts/ ディレクトリに .woff2 ファイルがなく、組み込みフォントでもない場合)、HTML を書く前にユーザーに警告します:「design.md は [フォント名] を指定していますが、フォントファイルが見つかりません。.woff2 ファイルを fonts/ に追加するか、最も近い組み込み代替フォントにフォールバックします。」
design.md が存在しない場合は、ユーザーに選択肢を提供します:
- ユーザーがスタイルや雰囲気を指定した場合? → visual-styles.md を読んで、8つの名前付きプリセットを確認します。最も近いものを選びます。
- オプションを視覚的に閲覧したい場合? → デザインピッカーを実行します:references/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 を書く前に — ステップ1からビジュアル ID を確認していることを確認してください。#333、#3b82f6、または Roboto に手を伸ばしている場合、スキップしています。
</ハードゲート>
アニメーションの前のレイアウト
すべての要素を、最も見える瞬間 — 完全に入り、正しく配置され、まだ退出していないフレーム — に配置すべき場所に配置します。これを最初に静的 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: それらのポジションにアニメーションイン
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);
// ステップ 4: それらのポジションからアニメーションアウト
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);
要素が時間を共有する場合
要素 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 | いいえ | 宣言された変数の JSON 配列(id/type/label/default)— 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":"タイトル","default":"こんにちは"},
{"id":"theme","type":"enum","label":"テーマ","default":"light","options":[
{"value":"light","label":"ライト"},
{"value":"dark","label":"ダーク"}
]}
]'
>
<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":"$99"}'
></div>
これらは同じ compositions/card.html ファイルを指しますが、getVariables() を介して異なる title と price を受け取ります。