Skip to content

Commit 33bfdcc

Browse files
miguel-heygenclaude
andcommitted
fix(skills): add rendering guardrails to hyperframes skill
Adds critical constraints discovered from eval analysis of 27 agent-generated compositions: - Ban `repeat: -1` (broke loading-spinner, prompt 20) - Ban async timeline construction (broke particle-logo, prompt 16) - Enforce minimum font sizes: 16px data labels, 20px body text (prompts 7, 8, 13, 14, 15, 19 all had illegible small text) - Ban full-screen dark linear gradients (prompts 3, 5, 10, 14 all showed H.264 color banding) - Mandate `<link>` font loading over CSS `@import` (prompts 7, 24 had font loading races) - Updated output checklist with all new constraints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 30fd02d commit 33bfdcc

1 file changed

Lines changed: 24 additions & 4 deletions

File tree

skills/hyperframes/SKILL.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,16 @@ Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
9292

9393
## Rules (Non-Negotiable)
9494

95-
**Deterministic:** No `Math.random()`, `Date.now()`, or time-based logic.
95+
**Deterministic:** No `Math.random()`, `Date.now()`, or time-based logic. Use a seeded PRNG if you need pseudo-random values (e.g. mulberry32).
9696

9797
**GSAP:** Only animate visual properties (`opacity`, `x`, `y`, `scale`, `rotation`, `color`, `backgroundColor`, `borderRadius`, transforms). Do NOT animate `visibility`, `display`, or call `video.play()`/`audio.play()`.
9898

9999
**Animation conflicts:** Never animate the same property on the same element from multiple timelines simultaneously.
100100

101+
**No `repeat: -1`:** Infinite-repeat timelines break the capture engine. Calculate the exact repeat count from composition duration: `repeat: Math.ceil(duration / cycleDuration) - 1`.
102+
103+
**Synchronous timeline construction:** Never build timelines inside `async`/`await`, `setTimeout`, or Promises. The capture engine reads `window.__timelines` synchronously after page load. If you need fonts loaded first, use a synchronous `document.fonts.load()` call or rely on `font-display: block` — the engine waits for the page `load` event.
104+
101105
**Never do:**
102106

103107
1. Forget `window.__timelines` registration
@@ -107,16 +111,27 @@ Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
107111
5. Animate video element dimensions — animate a wrapper div
108112
6. Call play/pause/seek on media — framework owns playback
109113
7. Create a top-level container without `data-composition-id`
114+
8. Use `repeat: -1` on any timeline or tween — always finite repeats
115+
9. Build timelines asynchronously (inside `async`, `setTimeout`, `Promise`)
110116

111117
## Typography and Assets
112118

113-
- Every composition loads its own fonts (`@import` or `@font-face`)
114-
- Use `font-display: block` for local fonts
119+
- Load fonts via `<link>` tags with `display=block` in `<head>`, NOT via CSS `@import` `@import` is async and may not complete before the first frame capture
120+
- Use `font-display: block` for `@font-face` declarations
115121
- Add `crossorigin="anonymous"` to external media
116-
- Minimum readable text: 20px landscape, 18px portrait
122+
- **Minimum font sizes for rendered video (1080p at DPR 1):**
123+
- Body/label text: 20px minimum (landscape), 18px minimum (portrait)
124+
- Data labels, axis labels, footnotes: 16px minimum — anything smaller becomes illegible after encoding
125+
- Headlines: 36px+ recommended
126+
- Avoid sub-14px text entirely — it will be unreadable in the final MP4
117127
- For dynamic text overflow, use `window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })` — returns `{ fontSize, fits }`
118128
- All files live at the project root alongside `index.html`; sub-compositions use `../`
119129

130+
### Backgrounds and Color
131+
132+
- **Avoid full-screen linear gradients on dark backgrounds** — H.264 encoding creates visible color banding. Prefer: solid colors, radial gradients with limited range, or subtle noise/texture overlays to break up banding.
133+
- For dark themes, use solid `#000` or `#0A0A0A` with localized radial glows rather than a linear gradient spanning the full viewport.
134+
120135
## Editing Existing Compositions
121136

122137
- Read the full composition first — match existing fonts, colors, animation patterns
@@ -129,6 +144,11 @@ Video must be `muted playsinline`. Audio is always a separate `<audio>` element:
129144
- [ ] Compositions in own HTML files, loaded via `data-composition-src`
130145
- [ ] `<template>` wrapper on sub-compositions
131146
- [ ] `window.__timelines` registered for every composition
147+
- [ ] Timeline construction is synchronous (no async/await wrapping timeline code)
148+
- [ ] No `repeat: -1` on any tween or nested timeline
149+
- [ ] No text below 16px (data labels, footnotes) or 20px (body text)
150+
- [ ] No full-screen linear dark gradients (use radial or solid + localized glow)
151+
- [ ] Fonts loaded via `<link>` with `display=block`, not CSS `@import`
132152
- [ ] 100% deterministic
133153
- [ ] Each composition includes GSAP script tag
134154
- [ ] `npx hyperframes lint` and `npx hyperframes validate` both pass

0 commit comments

Comments
 (0)