HyperFrames
HTML is the source of truth for video. A composition is an HTML file with
data-*
attributes for timing, a GSAP timeline for animation, and CSS for appearance. The framework handles clip visibility, media playback, and timeline sync.
Approach
Before writing HTML, think at a high level:
What
— what should the viewer experience? Identify the narrative arc, key moments, and emotional beats.
Structure
— how many compositions, which are sub-compositions vs inline, what tracks carry what (video, audio, overlays, captions).
Timing
— which clips drive the duration, where do transitions land, what's the pacing.
Layout
— build the end-state first. See "Layout Before Animation" below.
Animate
— then add motion using the rules below.
For small edits (fix a color, adjust timing, add one element), skip straight to the rules.
Visual Identity Gate
Check in this order:
DESIGN.md exists in the project?
→ Read it. Use its exact colors, fonts, motion rules, and "What NOT to Do" constraints.
visual-style.md exists?
→ Read it. Apply its
style_prompt_full
and structured fields. (Note:
visual-style.md
is a project-specific file.
visual-styles.md
is the style library with 8 named presets — different files.)
User named a style
(e.g., "Swiss Pulse", "dark and techy", "luxury brand")? → Read
visual-styles.md
for the 8 named presets. Generate a minimal DESIGN.md with:
Style Prompt
(one paragraph),
Colors
(3-5 hex values with roles),
Typography
(1-2 font families),
What NOT to Do
(3-5 anti-patterns).
None of the above?
→ Ask 3 questions before writing any HTML:
What's the mood? (explosive / cinematic / fluid / technical / chaotic / warm)
Light or dark canvas?
Any specific brand colors, fonts, or visual references?
Then generate a minimal DESIGN.md from the answers.
Every composition must trace its palette and typography back to a DESIGN.md, visual-style.md, or explicit user direction. If you're reaching for
333
,
3b82f6
, or
Roboto
— you skipped this step.
For motion defaults, sizing, entrance patterns, and easing — follow
house-style.md
. The house style handles HOW things move. The DESIGN.md handles WHAT things look like.
Layout Before Animation
Position every element where it should be at its
most visible moment
— the frame where it's fully entered, correctly placed, and not yet exiting. Write this as static HTML+CSS first. No GSAP yet.
Why this matters:
If you position elements at their animated start state (offscreen, scaled to 0, opacity 0) and tween them to where you think they should land, you're guessing the final layout. Overlaps are invisible until the video renders. By building the end state first, you can see and fix layout problems before adding any motion.
The process
Identify the hero frame
for each scene — the moment when the most elements are simultaneously visible. This is the layout you build.
Write static CSS
for that frame. The
.scene-content
container MUST fill the full scene using
width: 100%; height: 100%; padding: Npx;
with
display: flex; flex-direction: column; gap: Npx; box-sizing: border-box
. Use padding to push content inward — NEVER
position: absolute; top: Npx
on a content container. Absolute-positioned content containers overflow when content is taller than the remaining space. Reserve
position: absolute
for decoratives only.
Add entrances with
gsap.from()
— animate FROM offscreen/invisible TO the CSS position. The CSS position is the ground truth; the tween describes the journey to get there.
Add exits with
gsap.to()
— animate TO offscreen/invisible FROM the CSS position.
Example
/ scene-content fills the scene, padding positions content /
.scene-content
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
width
:
100
%
;
height
:
100
%
;
padding
:
120
px
160
px
;
gap
:
24
px
;
box-sizing
:
border-box
;
}
.title
{
font-size
:
120
px
;
}
.subtitle
{
font-size
:
42
px
;
}
/ Container fills any scene size (1920x1080, 1080x1920, etc).
Padding positions content. Flex + gap handles spacing. /
WRONG — hardcoded dimensions and absolute positioning:
.scene-content
{
position
:
absolute
;
top
:
200
px
;
left
:
160
px
;
width
:
1920
px
;
height
:
1080
px
;
display
:
flex
;
/ ... /
}
// Step 3: Animate INTO those positions
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
)
;
// Step 4: Animate OUT from those positions
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
)
;
When elements share space across time
If element A exits before element B enters in the same area, both should have correct CSS positions for their respective hero frames. The timeline ordering guarantees they never visually coexist — but if you skip the layout step, you won't catch the case where they accidentally overlap due to a timing error.
What counts as intentional overlap
Layered effects (glow behind text, shadow elements, background patterns) and z-stacked designs (card stacks, depth layers) are intentional. The layout step is about catching
unintentional
overlap — two headlines landing on top of each other, a stat covering a label, content bleeding off-frame.
Data Attributes
All Clips
Attribute
Required
Values
id
Yes
Unique identifier
data-start
Yes
Seconds or clip ID reference (
"el-1"
,
"intro + 2"
)
data-duration
Required for img/div/compositions
Seconds. Video/audio defaults to media duration.
data-track-index
Yes
Integer. Same-track clips cannot overlap.
data-media-start
No
Trim offset into source (seconds)
data-volume
No
0-1 (default 1)
data-track-index
does
not
affect visual layering — use CSS
z-index
.
Composition Clips
Attribute
Required
Values
data-composition-id
Yes
Unique composition ID
data-start
Yes
Start time (root composition: use
"0"
)
data-duration
Yes
Takes precedence over GSAP timeline duration
data-width
/
data-height
Yes
Pixel dimensions (1920x1080 or 1080x1920)
data-composition-src
No
Path to external HTML file
Composition Structure
Sub-compositions loaded via
data-composition-src
use a
wrapper.
Standalone compositions (the main index.html) do NOT use
— they put the
data-composition-id
div directly in
<body>
. Using
on a standalone file hides all content from the browser and breaks rendering.
Sub-composition structure:
<
template
id
=
"
my-comp-template
"
>
<
div
data-composition-id
=
"
my-comp
"
data-width
=
"
1920
"
data-height
=
"
1080
"
>
<
style
>
[
data-composition-id
=
"my-comp"
]
{
/* scoped styles */
}
style
>
<
script
src
=
"
https://cdn.jsdelivr.net/npm/gsap@3.14.2/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
>
Load in root:
Video and Audio
Video must be
muted playsinline
. Audio is always a separate
← 返回排行榜