Skills › HyperFrames › hyperframes-registry › templates.md
templates.md
417 lines · skills/hyperframes-registry/references/templates.md at a595755
1# Contribute Templates
3Copy-paste starter templates for each component type. These embed the proven patterns that pass `lint` and `check`.
Contribute Templates
Copy-paste starter templates for each component type. These embed the proven patterns that pass lint and check.
5## Caption Template
7```html
8<!doctype html>
9<html lang="en">
10 <head>
11 <meta charset="utf-8" />
12 <link
13 href="https://fonts.googleapis.com/css2?family=Montserrat:wght@800;900&display=swap"
14 rel="stylesheet"
15 />
16 <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
17 <style>
18 *,
19 *::before,
20 *::after {
21 margin: 0;
22 padding: 0;
23 box-sizing: border-box;
24 }
25 body {
26 background: #111;
27 overflow: hidden;
28 }
29 #root-BLOCKNAME {
30 position: relative;
31 width: 1920px;
32 height: 1080px;
33 overflow: hidden;
34 background: #111;
35 }
36 .cap-container {
37 position: absolute;
38 top: 0;
39 left: 0;
40 width: 100%;
41 height: 100%;
42 display: flex;
43 align-items: center;
44 justify-content: center;
45 }
46 .cg {
47 position: absolute;
48 display: flex;
49 align-items: center;
50 justify-content: center;
51 gap: 32px;
52 max-width: 1700px;
53 overflow: visible;
54 opacity: 0;
55 visibility: hidden;
56 }
57 .cw {
58 font-family: "Montserrat", sans-serif;
59 font-weight: 900;
60 font-size: 128px;
61 color: #ffffff;
62 text-transform: uppercase;
63 line-height: 1;
64 display: inline-block;
65 -webkit-text-stroke: 3px rgba(0, 0, 0, 0.8);
66 paint-order: stroke fill;
67 text-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
68 }
69 </style>
70 </head>
71 <body>
72 <div
73 id="root-BLOCKNAME"
74 data-composition-id="BLOCKNAME"
75 data-start="0"
76 data-duration="9"
77 data-width="1920"
78 data-height="1080"
79 >
80 <div class="cap-container" id="cc-BLOCKNAME"></div>
81 <div
82 id="drv-BLOCKNAME"
83 class="clip"
84 data-start="0"
85 data-duration="9"
86 data-track-index="0"
87 style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
88 ></div>
89 </div>
90 <script>
91 (function () {
92 window.__timelines = window.__timelines || {};
94 // REPLACE with actual transcript data
95 var WORDS = [
96 { text: "Welcome", start: 0.3, end: 0.65 },
97 { text: "to", start: 0.65, end: 0.8 },
98 { text: "the", start: 0.8, end: 0.95 },
99 { text: "future", start: 0.95, end: 1.4 },
100 // ... add all words
101 ];
103 var GROUPS = [
104 { start: 0.3, end: 1.3, wordStart: 0, wordEnd: 3, text: "Welcome to the future" },
105 // ... add all groups
106 ];
108 var container = document.getElementById("cc-BLOCKNAME");
110 GROUPS.forEach(function (g, gi) {
111 var groupEl = document.createElement("div");
112 groupEl.id = "PREFIX-cg-" + gi;
113 groupEl.className = "cg";
115 for (var wi = g.wordStart; wi <= g.wordEnd; wi++) {
116 var wordEl = document.createElement("span");
117 wordEl.id = "PREFIX-cw-" + wi;
118 wordEl.className = "cw";
119 wordEl.textContent = WORDS[wi].text;
120 groupEl.appendChild(wordEl);
121 }
123 // Pretext overflow prevention
124 if (window.__hyperframes && window.__hyperframes.fitTextFontSize) {
125 var _fit = window.__hyperframes.fitTextFontSize(g.text.toUpperCase(), {
126 fontFamily: "Montserrat",
127 fontWeight: 900,
128 maxWidth: 1550,
129 baseFontSize: 128,
130 minFontSize: 48,
131 });
132 if (_fit.fontSize < 128) {
133 for (var _fi = 0; _fi < groupEl.children.length; _fi++) {
134 groupEl.children[_fi].style.fontSize = _fit.fontSize + "px";
135 }
136 }
137 }
138 container.appendChild(groupEl);
139 });
141 var tl = gsap.timeline({ paused: true });
143 GROUPS.forEach(function (g, gi) {
144 var groupEl = document.getElementById("PREFIX-cg-" + gi);
146 // SHOW — set opacity to 1 (never use tl.from with opacity:0 here)
147 tl.set(groupEl, { opacity: 1, visibility: "visible" }, g.start);
149 // ENTRANCE — customize this per style
150 tl.from(groupEl, { scale: 1.3, duration: 0.15, ease: "back.out(2)" }, g.start);
152 // KARAOKE — highlight each word
153 for (var wi = g.wordStart; wi <= g.wordEnd; wi++) {
154 var wordEl = document.getElementById("PREFIX-cw-" + wi);
155 tl.to(wordEl, { color: "#FFD700", scale: 1.1, duration: 0.06 }, WORDS[wi].start);
156 tl.to(wordEl, { color: "#FFFFFF", scale: 1, duration: 0.08 }, WORDS[wi].end);
157 }
159 // EXIT
160 tl.to(groupEl, { opacity: 0, scale: 0.9, duration: 0.1 }, g.end - 0.1);
162 // HARD KILL (mandatory)
163 tl.set(groupEl, { opacity: 0, visibility: "hidden" }, g.end);
164 });
166 window.__timelines["BLOCKNAME"] = tl;
167 })();
168 </script>
169 </body>
170</html>
171```
173**Replace checklist:**
175- `BLOCKNAME` → your block name (e.g., `cap-swoosh`)
176- `PREFIX` → short unique prefix for IDs (e.g., `sw`)
177- Font family, weight, size → your style's typography
178- Entrance animation → your style's entrance
179- Karaoke highlight → your style's active word treatment
180- Colors → your style's palette
182---
Caption Template
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@800;900&display=swap"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #111;
overflow: hidden;
}
#root-BLOCKNAME {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #111;
}
.cap-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.cg {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
gap: 32px;
max-width: 1700px;
overflow: visible;
opacity: 0;
visibility: hidden;
}
.cw {
font-family: "Montserrat", sans-serif;
font-weight: 900;
font-size: 128px;
color: #ffffff;
text-transform: uppercase;
line-height: 1;
display: inline-block;
-webkit-text-stroke: 3px rgba(0, 0, 0, 0.8);
paint-order: stroke fill;
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div
id="root-BLOCKNAME"
data-composition-id="BLOCKNAME"
data-start="0"
data-duration="9"
data-width="1920"
data-height="1080"
>
<div class="cap-container" id="cc-BLOCKNAME"></div>
<div
id="drv-BLOCKNAME"
class="clip"
data-start="0"
data-duration="9"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// REPLACE with actual transcript data
var WORDS = [
{ text: "Welcome", start: 0.3, end: 0.65 },
{ text: "to", start: 0.65, end: 0.8 },
{ text: "the", start: 0.8, end: 0.95 },
{ text: "future", start: 0.95, end: 1.4 },
// ... add all words
];
var GROUPS = [
{ start: 0.3, end: 1.3, wordStart: 0, wordEnd: 3, text: "Welcome to the future" },
// ... add all groups
];
var container = document.getElementById("cc-BLOCKNAME");
GROUPS.forEach(function (g, gi) {
var groupEl = document.createElement("div");
groupEl.id = "PREFIX-cg-" + gi;
groupEl.className = "cg";
for (var wi = g.wordStart; wi <= g.wordEnd; wi++) {
var wordEl = document.createElement("span");
wordEl.id = "PREFIX-cw-" + wi;
wordEl.className = "cw";
wordEl.textContent = WORDS[wi].text;
groupEl.appendChild(wordEl);
}
// Pretext overflow prevention
if (window.__hyperframes && window.__hyperframes.fitTextFontSize) {
var _fit = window.__hyperframes.fitTextFontSize(g.text.toUpperCase(), {
fontFamily: "Montserrat",
fontWeight: 900,
maxWidth: 1550,
baseFontSize: 128,
minFontSize: 48,
});
if (_fit.fontSize < 128) {
for (var _fi = 0; _fi < groupEl.children.length; _fi++) {
groupEl.children[_fi].style.fontSize = _fit.fontSize + "px";
}
}
}
container.appendChild(groupEl);
});
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (g, gi) {
var groupEl = document.getElementById("PREFIX-cg-" + gi);
// SHOW — set opacity to 1 (never use tl.from with opacity:0 here)
tl.set(groupEl, { opacity: 1, visibility: "visible" }, g.start);
// ENTRANCE — customize this per style
tl.from(groupEl, { scale: 1.3, duration: 0.15, ease: "back.out(2)" }, g.start);
// KARAOKE — highlight each word
for (var wi = g.wordStart; wi <= g.wordEnd; wi++) {
var wordEl = document.getElementById("PREFIX-cw-" + wi);
tl.to(wordEl, { color: "#FFD700", scale: 1.1, duration: 0.06 }, WORDS[wi].start);
tl.to(wordEl, { color: "#FFFFFF", scale: 1, duration: 0.08 }, WORDS[wi].end);
}
// EXIT
tl.to(groupEl, { opacity: 0, scale: 0.9, duration: 0.1 }, g.end - 0.1);
// HARD KILL (mandatory)
tl.set(groupEl, { opacity: 0, visibility: "hidden" }, g.end);
});
window.__timelines["BLOCKNAME"] = tl;
})();
</script>
</body>
</html>Replace checklist:
BLOCKNAME→ your block name (e.g.,cap-swoosh)PREFIX→ short unique prefix for IDs (e.g.,sw)- Font family, weight, size → your style's typography
- Entrance animation → your style's entrance
- Karaoke highlight → your style's active word treatment
- Colors → your style's palette
Raw lines 5 to 183
184## VFX Template
186```html
187<!doctype html>
188<html lang="en">
189 <head>
190 <meta charset="utf-8" />
191 <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
192 <script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
193 <style>
194 *,
195 *::before,
196 *::after {
197 margin: 0;
198 padding: 0;
199 box-sizing: border-box;
200 }
201 body {
202 background: #030308;
203 overflow: hidden;
204 }
205 #root-BLOCKNAME {
206 position: relative;
207 width: 1920px;
208 height: 1080px;
209 overflow: hidden;
210 background: #030308;
211 }
212 #canvas-BLOCKNAME {
213 position: absolute;
214 top: 0;
215 left: 0;
216 width: 1920px;
217 height: 1080px;
218 }
219 </style>
220 </head>
221 <body>
222 <div
223 id="root-BLOCKNAME"
224 data-composition-id="BLOCKNAME"
225 data-start="0"
226 data-duration="10"
227 data-width="1920"
228 data-height="1080"
229 >
230 <canvas id="canvas-BLOCKNAME" width="1920" height="1080"></canvas>
231 <div
232 id="drv-BLOCKNAME"
233 class="clip"
234 data-start="0"
235 data-duration="10"
236 data-track-index="0"
237 style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
238 ></div>
239 </div>
240 <script>
241 (function () {
242 window.__timelines = window.__timelines || {};
244 // Seeded PRNG — NEVER use Math.random()
245 function mulberry32(a) {
246 return function () {
247 a |= 0;
248 a = (a + 0x6d2b79f5) | 0;
249 var t = Math.imul(a ^ (a >>> 15), 1 | a);
250 t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
251 return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
252 };
253 }
254 var rng = mulberry32(42);
256 var W = 1920,
257 H = 1080;
258 var canvas = document.getElementById("canvas-BLOCKNAME");
259 var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
260 renderer.setSize(W, H);
261 renderer.setPixelRatio(1);
262 renderer.toneMapping = THREE.ACESFilmicToneMapping;
263 renderer.toneMappingExposure = 1.1;
265 var scene = new THREE.Scene();
266 scene.background = new THREE.Color(0x030308);
267 var camera = new THREE.PerspectiveCamera(50, W / H, 0.1, 100);
268 camera.position.set(0, 0, 8);
270 // YOUR SCENE SETUP HERE
271 // - lights
272 // - geometry
273 // - materials
275 // State proxy — GSAP animates this, render reads it
276 var st = {
277 rotY: 0,
278 camZ: 8,
279 // add your animated properties
280 };
282 var tl = gsap.timeline({ paused: true });
284 // YOUR TWEENS HERE
285 tl.to(st, { rotY: Math.PI * 2, duration: 10, ease: "none" }, 0);
287 window.__timelines["BLOCKNAME"] = tl;
289 function renderScene() {
290 // Apply state to Three.js objects
291 camera.position.z = st.camZ;
292 // mesh.rotation.y = st.rotY;
294 renderer.render(scene, camera);
295 }
297 // Render via onUpdate — NO requestAnimationFrame
298 tl.eventCallback("onUpdate", renderScene);
299 renderScene();
300 })();
301 </script>
302 </body>
303</html>
304```
306**Replace checklist:**
308- `BLOCKNAME` → your block name (e.g., `vfx-chrome-blob`)
309- Scene setup → your geometry, lights, materials
310- State proxy → your animated properties
311- Tweens → your animation timeline
312- renderScene → apply state to your Three.js objects
314---
VFX Template
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #030308;
overflow: hidden;
}
#root-BLOCKNAME {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #030308;
}
#canvas-BLOCKNAME {
position: absolute;
top: 0;
left: 0;
width: 1920px;
height: 1080px;
}
</style>
</head>
<body>
<div
id="root-BLOCKNAME"
data-composition-id="BLOCKNAME"
data-start="0"
data-duration="10"
data-width="1920"
data-height="1080"
>
<canvas id="canvas-BLOCKNAME" width="1920" height="1080"></canvas>
<div
id="drv-BLOCKNAME"
class="clip"
data-start="0"
data-duration="10"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// Seeded PRNG — NEVER use Math.random()
function mulberry32(a) {
return function () {
a |= 0;
a = (a + 0x6d2b79f5) | 0;
var t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
var rng = mulberry32(42);
var W = 1920,
H = 1080;
var canvas = document.getElementById("canvas-BLOCKNAME");
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setSize(W, H);
renderer.setPixelRatio(1);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x030308);
var camera = new THREE.PerspectiveCamera(50, W / H, 0.1, 100);
camera.position.set(0, 0, 8);
// YOUR SCENE SETUP HERE
// - lights
// - geometry
// - materials
// State proxy — GSAP animates this, render reads it
var st = {
rotY: 0,
camZ: 8,
// add your animated properties
};
var tl = gsap.timeline({ paused: true });
// YOUR TWEENS HERE
tl.to(st, { rotY: Math.PI * 2, duration: 10, ease: "none" }, 0);
window.__timelines["BLOCKNAME"] = tl;
function renderScene() {
// Apply state to Three.js objects
camera.position.z = st.camZ;
// mesh.rotation.y = st.rotY;
renderer.render(scene, camera);
}
// Render via onUpdate — NO requestAnimationFrame
tl.eventCallback("onUpdate", renderScene);
renderScene();
})();
</script>
</body>
</html>Replace checklist:
BLOCKNAME→ your block name (e.g.,vfx-chrome-blob)- Scene setup → your geometry, lights, materials
- State proxy → your animated properties
- Tweens → your animation timeline
- renderScene → apply state to your Three.js objects
Raw lines 184 to 315
316## registry-item.json Templates
318**For blocks:**
320```json
321{
322 "$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
323 "name": "BLOCKNAME",
324 "type": "hyperframes:block",
325 "title": "Human-Readable Title",
326 "description": "One sentence: what it does and who uses it",
327 "dimensions": { "width": 1920, "height": 1080 },
328 "duration": 10,
329 "tags": ["category", "subcategory"],
330 "files": [
331 {
332 "path": "BLOCKNAME.html",
333 "target": "compositions/BLOCKNAME.html",
334 "type": "hyperframes:composition"
335 }
336 ]
337}
338```
340**For components** (no `dimensions` or `duration`):
342```json
343{
344 "$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
345 "name": "COMPONENTNAME",
346 "type": "hyperframes:component",
347 "title": "Human-Readable Title",
348 "description": "One sentence: what it does",
349 "tags": ["category"],
350 "files": [
351 {
352 "path": "COMPONENTNAME.html",
353 "target": "compositions/components/COMPONENTNAME.html",
354 "type": "hyperframes:snippet"
355 }
356 ]
357}
358```
360Tags by category:
362- Captions: `captions`, `viral`, `professional`, `karaoke`, `minimal`
363- VFX: `three-js`, `particles`, `shader`, `gpu`
364- Transitions: `transition`, `shader`, `wipe`, `dissolve`
365- Blocks: `lower-third`, `social`, `title-card`, `data-viz`
366- Components: `effect`, `overlay`, `text-treatment`
368---
registry-item.json Templates
For blocks:
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "BLOCKNAME",
"type": "hyperframes:block",
"title": "Human-Readable Title",
"description": "One sentence: what it does and who uses it",
"dimensions": { "width": 1920, "height": 1080 },
"duration": 10,
"tags": ["category", "subcategory"],
"files": [
{
"path": "BLOCKNAME.html",
"target": "compositions/BLOCKNAME.html",
"type": "hyperframes:composition"
}
]
}For components (no dimensions or duration):
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "COMPONENTNAME",
"type": "hyperframes:component",
"title": "Human-Readable Title",
"description": "One sentence: what it does",
"tags": ["category"],
"files": [
{
"path": "COMPONENTNAME.html",
"target": "compositions/components/COMPONENTNAME.html",
"type": "hyperframes:snippet"
}
]
}Tags by category:
- Captions:
captions,viral,professional,karaoke,minimal - VFX:
three-js,particles,shader,gpu - Transitions:
transition,shader,wipe,dissolve - Blocks:
lower-third,social,title-card,data-viz - Components:
effect,overlay,text-treatment
Raw lines 316 to 369
370## Component Template
372```html
373<!doctype html>
374<html lang="en">
375 <head>
376 <meta charset="utf-8" />
377 <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
378 <style>
379 *,
380 *::before,
381 *::after {
382 margin: 0;
383 padding: 0;
384 box-sizing: border-box;
385 }
386 body {
387 background: transparent;
388 overflow: hidden;
389 }
390 .COMPNAME-wrap {
391 position: absolute;
392 inset: 0;
393 overflow: hidden;
394 pointer-events: none;
395 }
396 </style>
397 </head>
398 <body>
399 <div class="COMPNAME-wrap">
400 <!-- Your reusable effect/overlay here -->
401 </div>
402 <script>
403 (function () {
404 // Component snippet — no data-composition-id, no __timelines.
405 // The parent composition controls timing.
406 // Keep all class names and IDs prefixed with COMPNAME.
407 })();
408 </script>
409 </body>
410</html>
411```
413**Replace checklist:**
415- `COMPNAME` → your component name (e.g., `shimmer-sweep`)
416- Background should be `transparent` so it overlays cleanly
417- No `data-composition-id` or `window.__timelines` — the parent owns timing
Component Template
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
.COMPNAME-wrap {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
</style>
</head>
<body>
<div class="COMPNAME-wrap">
<!-- Your reusable effect/overlay here -->
</div>
<script>
(function () {
// Component snippet — no data-composition-id, no __timelines.
// The parent composition controls timing.
// Keep all class names and IDs prefixed with COMPNAME.
})();
</script>
</body>
</html>Replace checklist:
COMPNAME→ your component name (e.g.,shimmer-sweep)- Background should be
transparentso it overlays cleanly - No
data-composition-idorwindow.__timelines— the parent owns timing