docs: root composition duration is compile-time, not script/--variables parameterizable (#1818)

* docs: clarify root composition duration is compile-time, not script/--variables parameterizable

* chore: regenerate skills-manifest for hyperframes-core docs change
This commit is contained in:
Miguel Ángel
2026-06-30 11:53:19 -07:00
committed by GitHub
parent f222af043e
commit f089db03ea
6 changed files with 43 additions and 12 deletions
+2 -1
View File
@@ -10,7 +10,7 @@ Hyperframes uses HTML data attributes to control timing, media playback, and [co
| Attribute | Example | Description |
|-----------|---------|-------------|
| `data-start` | `"0"` or `"intro"` | Start time in seconds, or a clip ID reference for [relative timing](#relative-timing) |
| `data-duration` | `"5"` | Duration in seconds. Required for images. Optional for video/audio (defaults to source duration). Not used on compositions. |
| `data-duration` | `"5"` | Duration in seconds. Required for images. Optional for video/audio (defaults to source duration). On the **root** composition it sets the total render length (see [Composition Attributes](#composition-attributes)); on nested sub-compositions it is ignored (their length comes from their child timeline). |
| `data-track-index` | `"0"` | Timeline track number. Controls z-ordering (higher = in front) and groups clips into rows. Clips on the same track cannot overlap. |
## Media Attributes
@@ -28,6 +28,7 @@ Hyperframes uses HTML data attributes to control timing, media playback, and [co
| `data-composition-id` | `"root"` | Unique ID for [composition](/concepts/compositions) wrapper (required on every composition) |
| `data-width` | `"1920"` | Composition width in pixels |
| `data-height` | `"1080"` | Composition height in pixels |
| `data-duration` (root) | `"30"` | On the **root** composition, the total render length / frame count in seconds. Read once from the source HTML at compile time, like `data-width` / `data-height`, so a script or `hyperframes render --variables` cannot change it (author it directly, one value per output). If the root omits `data-duration`, and only then, the renderer derives the total length from the live DOM / timeline after scripts run. |
| `data-composition-src` | `"./intro.html"` | Path to external [composition](/concepts/compositions) HTML file |
| `data-variable-values` | `'{"title":"Hello"}'` | JSON object of values passed to a nested composition. Inside the sub-composition, read them via `window.__hyperframes.getVariables()` — the runtime layers these over the sub-comp's own `data-composition-variables` defaults and exposes the merged result on a per-instance basis (the same source can be embedded multiple times with different values). |
| `data-composition-variables` | `'[{"id":"title","type":"string","label":"Title","default":"Hello"}]'` | JSON array of declared variables (`id`, `type`, `label`, `default`). Drives Studio editing UI and provides defaults read by `window.__hyperframes.getVariables()`. The CLI flag `hyperframes render --variables '<json>'` overrides these defaults at top-level render time; host elements override them per-instance via `data-variable-values`. |
+8 -3
View File
@@ -142,7 +142,11 @@ If you need to clamp or pin the clip to a specific length per render — for exa
</script>
```
The probe phase reads `data-duration` from the live DOM after your script runs, so an attribute written programmatically behaves identically to one baked into the source HTML.
The probe phase reads a **clip's** `data-duration` from the live DOM after your script runs, so an attribute written programmatically onto a clip or media element behaves identically to one baked into the source HTML.
<Warning>
This live-DOM re-read applies to clip and media elements, **not** to the **root composition's own `data-duration`** (its total render length / frame count). The renderer reads the root `data-duration` once at compile time, before your scripts run, exactly like `data-width` / `data-height`. If the root element carries a static `data-duration`, a script (or a variable) that rewrites it afterward is ignored, and the render uses the compile-time value. To make total render length vary per render, author the root `data-duration` directly (one value per output) rather than trying to drive it from a script. See [What can't be a variable](#what-cant-be-a-variable).
</Warning>
## What can't be a variable
@@ -150,12 +154,13 @@ A small set of inputs are read once from the source HTML or from the CLI / SDK,
| What | Mechanism (not a variable) |
|------|----------------------------|
| Composition dimensions | `data-width` / `data-height` on the composition element — parsed from the source HTML at compile time, not from the live DOM |
| Composition dimensions | `data-width` / `data-height` on the composition element, parsed from the source HTML at compile time, not from the live DOM |
| Root composition total duration | `data-duration` on the **root** composition element (the total render length / frame count), parsed from the source HTML at compile time. A static root `data-duration` is locked before scripts run, so neither a script nor a variable can change the render length. (A clip's own `data-duration` is different: it is re-read from the live DOM, as shown above.) |
| Frame rate | `--fps` flag on `hyperframes render`, or `config.fps` in the SDK |
| Output format / codec / quality | `--format` / `--codec` / `--quality` flags, or the SDK equivalents |
| A sibling or parent composition's variables | Variables are per-composition; use [`data-variable-values`](#per-instance-overrides-sub-compositions) on each sub-comp host element to pass overrides |
The deeper rule: variables are runtime values your script applies to the DOM. They can drive anything the renderer reads from the live DOM after that script runs — text, colors, media `src`, even clip `data-duration` as shown above. They can't change inputs the renderer reads once at compile time (dimensions) or that live entirely outside the composition (CLI flags, encoder settings).
The deeper rule: variables are runtime values your script applies to the DOM. They can drive anything the renderer reads from the live DOM after that script runs: text, colors, media `src`, even clip `data-duration` as shown above. They can't change inputs the renderer reads once at compile time (composition dimensions, and the root composition's total duration / render length) or that live entirely outside the composition (CLI flags, encoder settings).
## Reading Variables at Runtime
+24
View File
@@ -92,6 +92,30 @@ These are mistakes that cannot be caught by the linter. For automated checks, ru
</Tip>
</Accordion>
<Accordion title="Setting the root render length from a script or --variables does nothing">
**Symptom:** You set the **root** composition's `data-duration` from a script (or expose it as a variable and pass `--variables`), but the render length never changes. It stays locked at whatever `data-duration` is written in the source HTML.
**Cause:** The root composition's `data-duration` (the total render length / frame count) is read **once at compile time**, before your scripts run, exactly like `data-width` / `data-height`. A static root `data-duration` is locked at that point, so a later `root.setAttribute("data-duration", ...)` or a `--variables`-driven value is ignored. This is unlike a **clip's** `data-duration`, which the renderer re-reads from the live DOM after scripts run.
**Before (broken):**
```html index.html
<div id="root" data-composition-id="main" data-duration="2" data-width="1920" data-height="1080">
...
</div>
<script>
// Ignored: the root render length was already fixed at 2s at compile time.
document.getElementById("root").setAttribute("data-duration", "5");
</script>
```
**After (fixed):** author the root `data-duration` directly, one value per output. To produce a different length, author (or generate) a variant with that root `data-duration`. See [What can't be a variable](/concepts/variables#what-cant-be-a-variable).
<Tip>
Only the root composition's *total length* is compile-time-fixed. A clip's own `data-duration` (and a `<video>`/`<audio>` trim length) is re-read from the live DOM, so scripts and variables can still drive those, see [Swapping media: do you need to vary duration too?](/concepts/variables#swapping-media-do-you-need-to-vary-duration-too).
</Tip>
</Accordion>
<Accordion title="Missing class='clip' on timed elements">
**Symptom:** Elements are always visible, ignoring their `data-start` and `data-duration` timing.
+1 -1
View File
@@ -26,7 +26,7 @@
"files": 7
},
"hyperframes-core": {
"hash": "57d63243e8dcd11e",
"hash": "956d95de15d8c7fc",
"files": 13
},
"hyperframes-creative": {
@@ -6,13 +6,13 @@ Every HyperFrames composition uses `data-*` attributes to declare timing and str
Every renderable composition needs one root element:
| Attribute | Required | Meaning |
| ---------------------------- | -------- | -------------------------------------------------------------------------------- |
| `data-composition-id` | Yes | Unique ID. Must match the animation registry key on `window.__timelines`. |
| `data-width` / `data-height` | Yes | Pixel frame size. Common values: `1920x1080`, `1080x1920`, `1080x1080`. |
| `data-duration` | Yes | Duration in seconds. This is the render duration, not the GSAP timeline length. |
| `data-fps` | No | Optional frame rate hint. CLI render flags can override output fps. |
| `data-composition-variables` | No | JSON array of variable declarations (on `<html>`). See `variables-and-media.md`. |
| Attribute | Required | Meaning |
| ---------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data-composition-id` | Yes | Unique ID. Must match the animation registry key on `window.__timelines`. |
| `data-width` / `data-height` | Yes | Pixel frame size. Common values: `1920x1080`, `1080x1920`, `1080x1080`. |
| `data-duration` | Yes | Render duration in seconds (total length / frame count), not the GSAP timeline length. **Read once at compile time, like `data-width` / `data-height`**: a static root `data-duration` is locked before scripts run, so a script (`root.setAttribute("data-duration", ...)`) or a `--variables`-driven value cannot change the render length. To vary length per render, author the root `data-duration` directly. (A clip's `data-duration` is different: re-read from the live DOM, so scripts/variables can drive it.) Only when the root omits `data-duration` does the renderer derive total length from the live DOM / timeline after scripts run. |
| `data-fps` | No | Optional frame rate hint. CLI render flags can override output fps. |
| `data-composition-variables` | No | JSON array of variable declarations (on `<html>`). See `variables-and-media.md`. |
The root should be `position: relative`, have explicit pixel dimensions, and hide overflow unless intentionally composing outside the frame.
@@ -39,6 +39,7 @@ Also avoid:
Build the visible end-state in static HTML and CSS first, then animate from/to that state.
- The composition root has fixed pixel frame dimensions.
- **The root composition's total duration (render length / frame count) is fixed at compile time**, read once from the static root `data-duration` before scripts run, like `data-width` / `data-height`. A script or `--variables` value that rewrites the root `data-duration` afterward is ignored. To vary render length per output, author the root `data-duration` directly. (A _clip's_ own `data-duration` is re-read from the live DOM, so scripts/variables can still drive clip lengths. Only when the root omits `data-duration` does the renderer probe the live DOM / timeline for total length.)
- Scene containers should fill the scene with `width: 100%; height: 100%; box-sizing: border-box`.
- Use padding, flex, grid, and `max-width` for layout. Avoid positioning main content with hardcoded `top`/`left` offsets when a layout container can do it.
- Use `position: absolute` for layers and decorative elements, not as the default content-layout strategy.