Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/packages/core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ const meta: CompositionMetadata = extractCompositionMetadata(htmlString);
// data-composition-variables='[{"id":"title","label":"Title","type":"string","default":"Hello"}]'
// >

// Read resolved variables inside a composition (declared defaults +
// CLI overrides + per-instance host data-variable-values):
import { getVariables } from '@hyperframes/core';
const { title } = getVariables<{ title: string }>();

// Validate CLI / host overrides against the declared schema:
import { validateVariables, formatVariableValidationIssue } from '@hyperframes/core';
const issues = validateVariables({ title: 'Hello', count: 'three' }, meta.variables);
for (const issue of issues) {
console.warn(formatVariableValidationIssue(issue));
}

// Generate HTML from structured data
const html = generateHyperframesHtml(elements, {
animations,
Expand Down
42 changes: 35 additions & 7 deletions packages/cli/src/docs/compositions.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,42 @@ Use `npx hyperframes compositions` to see all compositions in a project.

## Variables

HyperFrames does not automatically bind `data-var-*` attributes into your composition DOM.
Two attributes with different shapes and different jobs:

- **`data-composition-variables`** on the `<html>` root — a JSON **array of declarations** (`{id, type, label, default}` per entry). Defines the schema: which variables exist, what type they are, and what defaults to use when no override is provided.
- **`data-variable-values`** on a sub-comp host element — a JSON **object keyed by variable id** (`{"title":"Pro","price":"$29"}`). Carries per-instance overrides for that one mount of the sub-composition.

They aren't redundant — one is "what variables does this composition have?" and the other is "what values should this particular embed use?" Inside any composition script, `window.__hyperframes.getVariables()` returns the merged result. Layering, lowest to highest precedence:

1. Declared defaults from `data-composition-variables`
2. Per-instance overrides from the host's `data-variable-values` (sub-comp embeds only)
3. CLI overrides from `npx hyperframes render --variables '{...}'` (top-level renders only)

```html
<!-- compositions/card.html -->
<html data-composition-variables='[
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is data-composition-variables but it says data-variable-values is this correct?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — data-variable-values (per-instance host override) and data-composition-variables (declaration on <html> root) are different attributes and the prose conflated them. Fixed in e5cbfe9: replaced the intro sentence with a two-bullet list naming each attribute and its role, then a follow-up explaining where the CLI --variables fits.

{"id":"title","type":"string","label":"Title","default":"Hello"},
{"id":"color","type":"color","label":"Color","default":"#111827"}
]'>
<body>
<div data-composition-id="card" data-width="1920" data-height="1080">
<h1 class="title"></h1>
<script>
const { title, color } = window.__hyperframes.getVariables();
document.querySelector(".title").textContent = title;
document.querySelector(".title").style.color = color;
</script>
</div>
</body>
</html>
```

```html
<div
data-composition-id="card"
data-composition-src="compositions/card.html"
data-variable-values='{"title":"Hello","color":"#ff4d4f"}'
></div>
<!-- index.html — embed twice with different per-instance values -->
<div data-composition-id="card-pro" data-composition-src="compositions/card.html"
data-variable-values='{"title":"Pro","color":"#ff4d4f"}'></div>
<div data-composition-id="card-enterprise" data-composition-src="compositions/card.html"
data-variable-values='{"title":"Enterprise","color":"#22c55e"}'></div>
```

Read `data-variable-values` inside the nested composition and apply the values in your own script. Variable metadata for tooling is declared separately via `data-composition-variables` and read with `extractCompositionMetadata()`.
The runtime layers `data-variable-values` over the sub-comp's declared defaults on a per-instance basis. The same `getVariables()` call works at the top level too — the CLI flag `--variables` provides the override, declared `default`s fall through for missing keys.
27 changes: 16 additions & 11 deletions skills/hyperframes-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,25 @@ npx hyperframes render --format webm # transparent WebM
npx hyperframes render --docker # byte-identical
```

| Flag | Options | Default | Notes |
| -------------- | --------------------- | -------------------------- | --------------------------- |
| `--output` | path | renders/name_timestamp.mp4 | Output path |
| `--fps` | 24, 30, 60 | 30 | 60fps doubles render time |
| `--quality` | draft, standard, high | standard | draft for iterating |
| `--format` | mp4, webm | mp4 | WebM supports transparency |
| `--workers` | 1-8 or auto | auto | Each spawns Chrome |
| `--docker` | flag | off | Reproducible output |
| `--gpu` | flag | off | GPU-accelerated encoding |
| `--strict` | flag | off | Fail on lint errors |
| `--strict-all` | flag | off | Fail on errors AND warnings |
| Flag | Options | Default | Notes |
| -------------------- | --------------------- | -------------------------- | ------------------------------------------------------------------ |
| `--output` | path | renders/name_timestamp.mp4 | Output path |
| `--fps` | 24, 30, 60 | 30 | 60fps doubles render time |
| `--quality` | draft, standard, high | standard | draft for iterating |
| `--format` | mp4, webm | mp4 | WebM supports transparency |
| `--workers` | 1-8 or auto | auto | Each spawns Chrome |
| `--docker` | flag | off | Reproducible output |
| `--gpu` | flag | off | GPU-accelerated encoding |
| `--strict` | flag | off | Fail on lint errors |
| `--strict-all` | flag | off | Fail on errors AND warnings |
| `--variables` | JSON object | — | Override variable values declared in `data-composition-variables` |
| `--variables-file` | path | — | JSON file with variable values (alternative to `--variables`) |
| `--strict-variables` | flag | off | Fail render on undeclared keys or type mismatches in `--variables` |

**Quality guidance:** `draft` while iterating, `standard` for review, `high` for final delivery.

**Parametrized renders:** the composition declares its variables on the `<html>` root with **`data-composition-variables`** — a JSON **array of declarations** (`{id, type, label, default}` per entry) that defines the schema. Scripts inside read the resolved values via `window.__hyperframes.getVariables()`. The CLI **`--variables '{"title":"Q4 Report"}'`** is a JSON **object keyed by id** that overrides those declared defaults for one render; missing keys fall through, so the same composition runs unchanged in dev preview and in production. (Sub-comp hosts can also override per-instance with **`data-variable-values`** — same object shape, scoped to one mount of the sub-composition. See the `hyperframes` skill for the full pattern.)

## Transcription

```bash
Expand Down
90 changes: 83 additions & 7 deletions skills/hyperframes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,20 @@ Layered effects (glow behind text, shadow elements, background patterns) and z-s

### 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 |
| 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 |
| `data-variable-values` | No | JSON object of per-instance variable overrides on a sub-comp host |

On the root `<html>` element:

| Attribute | Required | Values |
| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `data-composition-variables` | No | JSON array of declared variables (id/type/label/default) — drives Studio editing UI and provides defaults for `getVariables()` |

## Composition Structure

Expand Down Expand Up @@ -184,6 +191,75 @@ Sub-composition structure:

Load in 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>`

## Variables (Parametrized Compositions)

Render the same composition with different content — title, theme color, prices, captions — without editing the source HTML.

**Three-step pattern:**

1. **Declare** variables on the composition's `<html>` root with `data-composition-variables`. Each entry needs `id`, `type` (one of `string`, `number`, `color`, `boolean`, `enum`), `label`, and `default`. Enum entries also need `options: [{value, label}, ...]`.
2. **Read** the resolved values inside the composition's script with `window.__hyperframes.getVariables()`. Returns the merged result of declared defaults + per-instance overrides + CLI overrides.
3. **Override** at render time with `npx hyperframes render --variables '{...}'` (top-level) or with `data-variable-values='{...}'` on the host element (per-instance for sub-comps).

```html
<!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>
```

```bash
# Dev preview uses declared defaults
npx hyperframes preview

# Render with overrides
npx hyperframes render --variables '{"title":"Q4 Report","theme":"dark"}' --output q4.mp4

# Or from a JSON file
npx hyperframes render --variables-file ./vars.json
```

**Sub-composition per-instance values:** the same `getVariables()` works inside sub-comps loaded via `data-composition-src`. Each host element passes its own values:

```html
<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":"Custom"}'
></div>
```

The runtime layers each host's `data-variable-values` over the sub-comp's declared defaults on a per-instance basis, so the same source can be embedded multiple times with different content.

**Rules of thumb:**

- Always provide a sensible `default` for every declared variable. Dev preview uses defaults — without them, the composition won't render correctly until `--variables` is provided.
- Read variables once at the top of the script (`const { title } = ...`), not inside frame loops or event handlers — `getVariables()` allocates a fresh object per call.
- Use `--strict-variables` in CI to fail fast on undeclared keys or type mismatches.
- Variable types are validated at render time. `string`, `number`, `boolean`, and `color` (hex string) check `typeof`; `enum` checks the value is in the declared `options`.

## Video and Audio

Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
Expand Down
Loading