Files
hyperframes/skills/remotion-to-hyperframes/references/parameters.md
T
James 890f305cd1 feat(skills): remotion-to-hyperframes references (6/7)
Adds 11 progressively-disclosed reference files that the skill loads on
demand during translation. Total ~1500 LOC, every file under 200 lines
(skill-creator's progressive-disclosure budget).

  api-map.md         the comprehensive Remotion -> HF translation table
                     (the index; loaded at start of translation)
  timing.md          interpolate, spring (validated configs), easing,
                     count-up, stagger
  sequencing.md      Sequence, Series, Loop, Freeze, AbsoluteFill,
                     Composition root
  media.md           Audio, Video, Img, IFrame, OffthreadVideo,
                     staticFile, asset paths
  transitions.md     @remotion/transitions presentations -> manual GSAP
                     crossfades or HF shader-transitions
  lottie.md          @remotion/lottie -> HF lottie adapter (incl. AE
                     feature limitations note)
  fonts.md           Google Fonts loading, local @font-face, system
                     fallback noise floor
  parameters.md      Zod schemas, defaultProps, sync vs async
                     calculateMetadata
  escape-hatch.md    when to bow out + the runtime interop pattern
                     from PR #214
  limitations.md     known caveat patterns (volume ramps, Loop with
                     state, custom presentations, code-split components)
  eval.md            how to run the validation harness, threshold rule
                     of thumb, what the noise floor looks like

The references are evidence-driven rather than speculative: every spring
config, easing curve, and SSIM threshold is documented from the
validated T1/T2/T3 calibration runs (mean 0.974 / 0.985 / 0.953). The
escape-hatch boundaries match the lint blockers in PR 2 and the T4
fixtures in PR 5.

Replaces the placeholder .gitkeep from PR 1.
2026-04-27 23:55:51 +00:00

4.5 KiB

Parameter translation: Zod schemas, defaultProps, calculateMetadata

How a typed Remotion <Composition schema={...} defaultProps={...} /> turns into a parameterized HF composition.

Sync calculateMetadata (translatable)

<Composition
  id="MyVideo"
  component={MyVideo}
  schema={z.object({ title: z.string(), duration: z.number() })}
  defaultProps={{ title: "Hello", duration: 90 }}
  calculateMetadata={({ props }) => ({
    durationInFrames: props.duration,
    fps: 30,
  })}
/>

When calculateMetadata is synchronous and only uses props, resolve it at translation time — call it with defaultProps (or whatever the caller specifies) and write the concrete result into the HTML:

<div
  id="stage"
  data-composition-id="MyVideo"
  data-start="0"
  data-duration="3"          <!-- 90/30 -->
  data-fps="30"
  data-title="Hello"
></div>

The data-title attribute carries the value through. Code that originally read props.title reads document.getElementById("stage").dataset.title in HF.

Async calculateMetadata (NOT translatable)

<Composition
  calculateMetadata={async ({ props }) => {
    const res = await fetch(...);
    return { durationInFrames: res.duration };
  }}
/>

Refuse + interop. HF needs composition metadata up-front to seed the HTML. Resolving network calls at translation time defeats the purpose of having dynamic metadata. See escape-hatch.md.

The lint rule r2hf/async-metadata catches this. T4 case 03 tests it.

Default props

defaultProps={{
  title: "Hello",
  subtitle: "World",
  count: 42,
}}

Translate to data-* attributes on the root #stage div:

<div id="stage" data-title="Hello" data-subtitle="World" data-count="42">...</div>

Convention: propNamedata-prop-name (kebab-case). Inside the GSAP script, read via document.getElementById("stage").dataset.propName.

Nested object / array props

defaultProps={{
  stats: [
    { label: "Stars", value: 1247, color: "#fbbf24" },
    { label: "Forks", value: 312, color: "#60a5fa" },
  ],
}}

Don't try to encode the array as a JSON data- attribute — HF's runtime doesn't parse those. Materialize the array as repeated HTML markup:

<div id="scene-stats">
  <div class="stat-card" data-stat-index="0" data-stat-value="1247" style="--card-color:#fbbf24">
    <div class="number">0</div>
    <div class="label">Stars</div>
  </div>
  <div class="stat-card" data-stat-index="1" data-stat-value="312" style="--card-color:#60a5fa">
    <div class="number">0</div>
    <div class="label">Forks</div>
  </div>
</div>

The component template (StatCard.tsx) becomes the markup template; each instance gets its scalar props rendered as data-* and CSS custom properties.

Validated in T3 — three StatCards reused with different props, mean SSIM 0.953.

Numeric props that need typed parsing

document.getElementById("stage").dataset.count is a string. Convert at read time:

const count = Number(stage.dataset.count);

Or inline values directly into the GSAP script when the data is known at translation time and doesn't need to vary per render.

Boolean props

defaultProps={{ darkMode: true }}

Two conventions:

  • data-dark-mode="true" — read as string, compare === "true"
  • data-dark-mode (presence/absence) — <div data-dark-mode> for true, omit for false

Pick one and be consistent. The presence/absence form is HTML-idiomatic and pairs well with CSS attribute selectors:

[data-dark-mode] .scene {
  background: #000;
}

Zod runtime validation

Remotion's schema validates props at composition load. HF doesn't have an equivalent — by the time the HTML is in the renderer, the schema is already gone.

Validate at translation time instead. If the user passes invalid data, fail with a translation error before emitting HTML. This matches Zod's "fail loud" intent without requiring the runtime dependency.

When the composition uses props for computed prop derivation

const Composition: React.FC<Props> = ({ stats }) => {
  const total = stats.reduce((acc, s) => acc + s.value, 0);
  return <div>{total}</div>;
};

Compute the derived value at translation time and bake it into the HTML or a data- attribute. Don't try to express the computation in JS in the HF composition — that adds runtime overhead and makes the HTML stateful in ways that complicate human editing.

If the derivation is non-trivial (involves the array itself, not just scalars), materialize it as static text in the HTML.