Skill Tracer
Skills › HyperFrames › hyperframes-registry › templates.md

templates.md

417 lines · skills/hyperframes-registry/references/templates.md at a595755

Open on GitHub
1# Contribute Templates
2
3Copy-paste starter templates for each component type. These embed the proven patterns that pass `lint` and `check`.
4

Contribute Templates

Copy-paste starter templates for each component type. These embed the proven patterns that pass lint and check.

Raw lines 1 to 4
5## Caption Template
6
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
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 ]
338```
340**For components** (no `dimensions` or `duration`):
342```json
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 ]
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 transparent so it overlays cleanly
  • No data-composition-id or window.__timelines — the parent owns timing
Raw lines 370 to 417