Skip to content

Commit 62c2589

Browse files
authored
docs(skills): document variables system in SKILL.md + docs (PR 4/4) (#603)
## What Distribution PR for the variables feature stack. Tells agents how to declare, read, and override variables across the four authoring surfaces — the two skill files agents load (`hyperframes`, `hyperframes-cli`), the public docs (`docs/packages/core.mdx`), and the in-CLI docs (`npx hyperframes docs compositions`). This is **PR 4 of 4**, the final PR in the stack. Stacked on `feat/get-variables-validation` (PR #602). ## Why PRs 1–3 added the runtime helper, sub-comp scoping, and schema validation, but the only places that mention them are the docs in those PRs. Agents loading `/hyperframes` or `/hyperframes-cli` skills won't know the new attributes/flags exist. This PR closes the loop. ## How - **`skills/hyperframes/SKILL.md`** — new "Variables (Parametrized Compositions)" section right after "Composition Structure" with: declare/read/override pattern, full worked example (with enum), sub-comp per-instance pattern (two hosts sharing a source), rules of thumb (defaults always, read-once, `--strict-variables` in CI, type validation behavior). Also added `data-variable-values` + `data-composition-variables` rows to the existing data-attributes tables. - **`skills/hyperframes-cli/SKILL.md`** — added `--variables`, `--variables-file`, `--strict-variables` to the render flag table; short paragraph forwarding to the hyperframes skill for the full pattern. - **`docs/packages/core.mdx`** — added a code snippet showing `getVariables<T>()` and `validateVariables` / `formatVariableValidationIssue` for tooling that validates CLI / host overrides. - **`packages/cli/src/docs/compositions.md`** — replaced the obsolete `JSON.parse(host.dataset.variableValues)` example with the modern `getVariables()` pattern. The `openai/plugins` mirror is intentionally out of scope. Skills in this repo are the source of truth; the downstream mirror is updated after each release as a separate workflow. ## Test plan - [x] Doc-only PR — no source code, no tests. - [x] Format check passes via `bunx oxfmt --check` on the touched markdown files. - [x] Manual review confirms each example compiles in head against the runtime/CLI surface that PRs 1–3 ship. ## Backwards compatibility Doc-only — no behavior change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents 1c9d726 + 21243b6 commit 62c2589

4 files changed

Lines changed: 146 additions & 25 deletions

File tree

docs/packages/core.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ const meta: CompositionMetadata = extractCompositionMetadata(htmlString);
130130
// data-composition-variables='[{"id":"title","label":"Title","type":"string","default":"Hello"}]'
131131
// >
132132

133+
// Read resolved variables inside a composition (declared defaults +
134+
// CLI overrides + per-instance host data-variable-values):
135+
import { getVariables } from '@hyperframes/core';
136+
const { title } = getVariables<{ title: string }>();
137+
138+
// Validate CLI / host overrides against the declared schema:
139+
import { validateVariables, formatVariableValidationIssue } from '@hyperframes/core';
140+
const issues = validateVariables({ title: 'Hello', count: 'three' }, meta.variables);
141+
for (const issue of issues) {
142+
console.warn(formatVariableValidationIssue(issue));
143+
}
144+
133145
// Generate HTML from structured data
134146
const html = generateHyperframesHtml(elements, {
135147
animations,

packages/cli/src/docs/compositions.md

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,42 @@ Use `npx hyperframes compositions` to see all compositions in a project.
2626

2727
## Variables
2828

29-
HyperFrames does not automatically bind `data-var-*` attributes into your composition DOM.
29+
Two attributes with different shapes and different jobs:
30+
31+
- **`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.
32+
- **`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.
33+
34+
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:
35+
36+
1. Declared defaults from `data-composition-variables`
37+
2. Per-instance overrides from the host's `data-variable-values` (sub-comp embeds only)
38+
3. CLI overrides from `npx hyperframes render --variables '{...}'` (top-level renders only)
39+
40+
```html
41+
<!-- compositions/card.html -->
42+
<html data-composition-variables='[
43+
{"id":"title","type":"string","label":"Title","default":"Hello"},
44+
{"id":"color","type":"color","label":"Color","default":"#111827"}
45+
]'>
46+
<body>
47+
<div data-composition-id="card" data-width="1920" data-height="1080">
48+
<h1 class="title"></h1>
49+
<script>
50+
const { title, color } = window.__hyperframes.getVariables();
51+
document.querySelector(".title").textContent = title;
52+
document.querySelector(".title").style.color = color;
53+
</script>
54+
</div>
55+
</body>
56+
</html>
57+
```
3058

3159
```html
32-
<div
33-
data-composition-id="card"
34-
data-composition-src="compositions/card.html"
35-
data-variable-values='{"title":"Hello","color":"#ff4d4f"}'
36-
></div>
60+
<!-- index.html — embed twice with different per-instance values -->
61+
<div data-composition-id="card-pro" data-composition-src="compositions/card.html"
62+
data-variable-values='{"title":"Pro","color":"#ff4d4f"}'></div>
63+
<div data-composition-id="card-enterprise" data-composition-src="compositions/card.html"
64+
data-variable-values='{"title":"Enterprise","color":"#22c55e"}'></div>
3765
```
3866

39-
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()`.
67+
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.

skills/hyperframes-cli/SKILL.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,25 @@ npx hyperframes render --format webm # transparent WebM
101101
npx hyperframes render --docker # byte-identical
102102
```
103103

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

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

121+
**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.)
122+
118123
## Transcription
119124

120125
```bash

skills/hyperframes/SKILL.md

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,20 @@ Layered effects (glow behind text, shadow elements, background patterns) and z-s
148148

149149
### Composition Clips
150150

151-
| Attribute | Required | Values |
152-
| ---------------------------- | -------- | -------------------------------------------- |
153-
| `data-composition-id` | Yes | Unique composition ID |
154-
| `data-start` | Yes | Start time (root composition: use `"0"`) |
155-
| `data-duration` | Yes | Takes precedence over GSAP timeline duration |
156-
| `data-width` / `data-height` | Yes | Pixel dimensions (1920x1080 or 1080x1920) |
157-
| `data-composition-src` | No | Path to external HTML file |
151+
| Attribute | Required | Values |
152+
| ---------------------------- | -------- | ----------------------------------------------------------------- |
153+
| `data-composition-id` | Yes | Unique composition ID |
154+
| `data-start` | Yes | Start time (root composition: use `"0"`) |
155+
| `data-duration` | Yes | Takes precedence over GSAP timeline duration |
156+
| `data-width` / `data-height` | Yes | Pixel dimensions (1920x1080 or 1080x1920) |
157+
| `data-composition-src` | No | Path to external HTML file |
158+
| `data-variable-values` | No | JSON object of per-instance variable overrides on a sub-comp host |
159+
160+
On the root `<html>` element:
161+
162+
| Attribute | Required | Values |
163+
| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
164+
| `data-composition-variables` | No | JSON array of declared variables (id/type/label/default) — drives Studio editing UI and provides defaults for `getVariables()` |
158165

159166
## Composition Structure
160167

@@ -184,6 +191,75 @@ Sub-composition structure:
184191

185192
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>`
186193

194+
## Variables (Parametrized Compositions)
195+
196+
Render the same composition with different content — title, theme color, prices, captions — without editing the source HTML.
197+
198+
**Three-step pattern:**
199+
200+
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}, ...]`.
201+
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.
202+
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).
203+
204+
```html
205+
<!doctype html>
206+
<html
207+
data-composition-variables='[
208+
{"id":"title","type":"string","label":"Title","default":"Hello"},
209+
{"id":"theme","type":"enum","label":"Theme","default":"light","options":[
210+
{"value":"light","label":"Light"},
211+
{"value":"dark","label":"Dark"}
212+
]}
213+
]'
214+
>
215+
<body>
216+
<div data-composition-id="root" data-width="1920" data-height="1080">
217+
<h1 id="hero" class="clip" data-start="0" data-duration="3"></h1>
218+
<script>
219+
const { title, theme } = window.__hyperframes.getVariables();
220+
document.getElementById("hero").textContent = title;
221+
document.body.dataset.theme = theme;
222+
</script>
223+
</div>
224+
</body>
225+
</html>
226+
```
227+
228+
```bash
229+
# Dev preview uses declared defaults
230+
npx hyperframes preview
231+
232+
# Render with overrides
233+
npx hyperframes render --variables '{"title":"Q4 Report","theme":"dark"}' --output q4.mp4
234+
235+
# Or from a JSON file
236+
npx hyperframes render --variables-file ./vars.json
237+
```
238+
239+
**Sub-composition per-instance values:** the same `getVariables()` works inside sub-comps loaded via `data-composition-src`. Each host element passes its own values:
240+
241+
```html
242+
<div
243+
data-composition-id="card-pro"
244+
data-composition-src="compositions/card.html"
245+
data-variable-values='{"title":"Pro","price":"$29"}'
246+
></div>
247+
<div
248+
data-composition-id="card-enterprise"
249+
data-composition-src="compositions/card.html"
250+
data-variable-values='{"title":"Enterprise","price":"Custom"}'
251+
></div>
252+
```
253+
254+
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.
255+
256+
**Rules of thumb:**
257+
258+
- 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.
259+
- 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.
260+
- Use `--strict-variables` in CI to fail fast on undeclared keys or type mismatches.
261+
- 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`.
262+
187263
## Video and Audio
188264

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

0 commit comments

Comments
 (0)