dsh-hyperframes 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.en.md +35 -0
  2. package/README.md +35 -0
  3. package/cordis.patch.yml +7 -0
  4. package/lib/index.d.ts +35 -0
  5. package/lib/index.js +74 -0
  6. package/package.json +63 -0
  7. package/skills/gsap/SKILL.md +213 -0
  8. package/skills/gsap/references/effects.md +297 -0
  9. package/skills/gsap/scripts/extract-audio-data.py +188 -0
  10. package/skills/hyperframes/SKILL.md +366 -0
  11. package/skills/hyperframes/data-in-motion.md +19 -0
  12. package/skills/hyperframes/house-style.md +71 -0
  13. package/skills/hyperframes/palettes/bold-energetic.md +14 -0
  14. package/skills/hyperframes/palettes/clean-corporate.md +14 -0
  15. package/skills/hyperframes/palettes/dark-premium.md +14 -0
  16. package/skills/hyperframes/palettes/jewel-rich.md +14 -0
  17. package/skills/hyperframes/palettes/monochrome.md +14 -0
  18. package/skills/hyperframes/palettes/nature-earth.md +14 -0
  19. package/skills/hyperframes/palettes/neon-electric.md +14 -0
  20. package/skills/hyperframes/palettes/pastel-soft.md +14 -0
  21. package/skills/hyperframes/palettes/warm-editorial.md +14 -0
  22. package/skills/hyperframes/patterns.md +118 -0
  23. package/skills/hyperframes/references/audio-reactive.md +76 -0
  24. package/skills/hyperframes/references/captions.md +132 -0
  25. package/skills/hyperframes/references/css-patterns.md +373 -0
  26. package/skills/hyperframes/references/dynamic-techniques.md +90 -0
  27. package/skills/hyperframes/references/motion-principles.md +69 -0
  28. package/skills/hyperframes/references/transcript-guide.md +151 -0
  29. package/skills/hyperframes/references/transitions/catalog.md +117 -0
  30. package/skills/hyperframes/references/transitions/css-3d.md +12 -0
  31. package/skills/hyperframes/references/transitions/css-blur.md +51 -0
  32. package/skills/hyperframes/references/transitions/css-cover.md +43 -0
  33. package/skills/hyperframes/references/transitions/css-destruction.md +95 -0
  34. package/skills/hyperframes/references/transitions/css-dissolve.md +66 -0
  35. package/skills/hyperframes/references/transitions/css-distortion.md +45 -0
  36. package/skills/hyperframes/references/transitions/css-grid.md +10 -0
  37. package/skills/hyperframes/references/transitions/css-light.md +49 -0
  38. package/skills/hyperframes/references/transitions/css-mechanical.md +30 -0
  39. package/skills/hyperframes/references/transitions/css-other.md +25 -0
  40. package/skills/hyperframes/references/transitions/css-push.md +41 -0
  41. package/skills/hyperframes/references/transitions/css-radial.md +37 -0
  42. package/skills/hyperframes/references/transitions/css-scale.md +24 -0
  43. package/skills/hyperframes/references/transitions.md +112 -0
  44. package/skills/hyperframes/references/tts.md +75 -0
  45. package/skills/hyperframes/references/typography.md +175 -0
  46. package/skills/hyperframes/scripts/animation-map.mjs +596 -0
  47. package/skills/hyperframes/scripts/contrast-report.mjs +335 -0
  48. package/skills/hyperframes/visual-styles.md +211 -0
  49. package/skills/hyperframes-cli/SKILL.md +152 -0
  50. package/skills/hyperframes-registry/SKILL.md +106 -0
  51. package/skills/hyperframes-registry/examples/add-block.md +51 -0
  52. package/skills/hyperframes-registry/examples/add-component.md +73 -0
  53. package/skills/hyperframes-registry/references/demo-html-pattern.md +54 -0
  54. package/skills/hyperframes-registry/references/discovery.md +53 -0
  55. package/skills/hyperframes-registry/references/install-locations.md +45 -0
  56. package/skills/hyperframes-registry/references/wiring-blocks.md +91 -0
  57. package/skills/hyperframes-registry/references/wiring-components.md +77 -0
  58. package/skills/website-to-hyperframes/SKILL.md +125 -0
  59. package/skills/website-to-hyperframes/references/step-1-capture.md +74 -0
  60. package/skills/website-to-hyperframes/references/step-2-design.md +178 -0
  61. package/skills/website-to-hyperframes/references/step-3-script.md +96 -0
  62. package/skills/website-to-hyperframes/references/step-4-storyboard.md +246 -0
  63. package/skills/website-to-hyperframes/references/step-5-vo.md +42 -0
  64. package/skills/website-to-hyperframes/references/step-6-build.md +229 -0
  65. package/skills/website-to-hyperframes/references/step-7-validate.md +114 -0
  66. package/skills/website-to-hyperframes/references/techniques.md +387 -0
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+ // contrast-report.mjs — HyperFrames contrast audit
3
+ //
4
+ // Reads a composition, seeks to N sample timestamps, walks the DOM for text
5
+ // elements, measures the WCAG 2.1 contrast ratio between each element's
6
+ // declared foreground color and the pixels behind it, and emits:
7
+ //
8
+ // - contrast-report.json (machine-readable, one entry per text element × sample)
9
+ // - contrast-overlay.png (sprite grid; magenta=fail AA, yellow=pass AA only, green=AAA)
10
+ //
11
+ // Usage:
12
+ // node skills/hyperframes/scripts/contrast-report.mjs <composition-dir> \
13
+ // [--samples N] [--out <dir>] [--width W] [--height H] [--fps N]
14
+ //
15
+ // The composition directory must contain an index.html. Raw authoring HTML
16
+ // works — the producer's file server auto-injects the runtime at serve time.
17
+ // Exits 1 if any text element fails WCAG AA.
18
+
19
+ import { mkdir, writeFile } from "node:fs/promises";
20
+ import { resolve } from "node:path";
21
+
22
+ import sharp from "sharp";
23
+
24
+ // Use the producer's file server — it auto-injects the HyperFrames runtime
25
+ // and render-seek bridge, so raw authoring HTML works without a build step.
26
+ import {
27
+ createFileServer,
28
+ createCaptureSession,
29
+ initializeSession,
30
+ closeCaptureSession,
31
+ captureFrameToBuffer,
32
+ getCompositionDuration,
33
+ } from "@hyperframes/producer";
34
+
35
+ // ─── CLI ─────────────────────────────────────────────────────────────────────
36
+
37
+ const args = parseArgs(process.argv.slice(2));
38
+ if (!args.composition) die("missing <composition-dir>");
39
+
40
+ const SAMPLES = Number(args.samples ?? 10);
41
+ const OUT_DIR = resolve(args.out ?? ".hyperframes/contrast");
42
+ const WIDTH = Number(args.width ?? 1920);
43
+ const HEIGHT = Number(args.height ?? 1080);
44
+ const FPS = Number(args.fps ?? 30);
45
+ const COMP_DIR = resolve(args.composition);
46
+
47
+ // ─── Main ────────────────────────────────────────────────────────────────────
48
+
49
+ await mkdir(OUT_DIR, { recursive: true });
50
+
51
+ const server = await createFileServer({ projectDir: COMP_DIR, port: 0 });
52
+ const session = await createCaptureSession(
53
+ server.url,
54
+ OUT_DIR,
55
+ { width: WIDTH, height: HEIGHT, fps: FPS, format: "png" },
56
+ null,
57
+ );
58
+ await initializeSession(session);
59
+
60
+ try {
61
+ const duration = await getCompositionDuration(session);
62
+ const times = Array.from(
63
+ { length: SAMPLES },
64
+ (_, i) => +(((i + 0.5) / SAMPLES) * duration).toFixed(3),
65
+ );
66
+
67
+ const allEntries = [];
68
+ const overlayFrames = [];
69
+
70
+ for (let i = 0; i < times.length; i++) {
71
+ const t = times[i];
72
+ const { buffer: pngBuf } = await captureFrameToBuffer(session, i, t);
73
+ const elements = await probeTextElements(session, t);
74
+ const annotated = await annotateFrame(pngBuf, elements);
75
+ overlayFrames.push({ t, png: annotated });
76
+ for (const el of elements) allEntries.push({ time: t, ...el });
77
+ }
78
+
79
+ const report = {
80
+ composition: COMP_DIR,
81
+ width: WIDTH,
82
+ height: HEIGHT,
83
+ duration,
84
+ samples: times,
85
+ entries: allEntries,
86
+ summary: summarize(allEntries),
87
+ };
88
+
89
+ await writeFile(resolve(OUT_DIR, "contrast-report.json"), JSON.stringify(report, null, 2));
90
+ await writeOverlaySprite(overlayFrames, resolve(OUT_DIR, "contrast-overlay.png"));
91
+
92
+ printSummary(report);
93
+ process.exitCode = report.summary.failAA > 0 ? 1 : 0;
94
+ } finally {
95
+ await closeCaptureSession(session).catch(() => {});
96
+ server.close();
97
+ }
98
+
99
+ // ─── DOM probe (runs in the page) ────────────────────────────────────────────
100
+
101
+ async function probeTextElements(session, _t) {
102
+ // `session.page` is the Puppeteer Page owned by the capture session.
103
+ // We pass a pure function to `evaluate`: it walks the DOM and returns
104
+ // enough info for us to compute a ratio in Node using the frame buffer.
105
+ return await session.page.evaluate(() => {
106
+ /** @type {Array<{selector: string, text: string, fg: [number,number,number,number], fontSize: number, fontWeight: number, bbox: {x:number,y:number,w:number,h:number}}>} */
107
+ const out = [];
108
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);
109
+ const parseColor = (c) => {
110
+ const m = c.match(/rgba?\(([^)]+)\)/);
111
+ if (!m) return [0, 0, 0, 1];
112
+ const parts = m[1].split(",").map((s) => parseFloat(s.trim()));
113
+ return [parts[0], parts[1], parts[2], parts[3] ?? 1];
114
+ };
115
+ const selectorOf = (el) => {
116
+ if (el.id) return `#${el.id}`;
117
+ const cls = [...el.classList].slice(0, 2).join(".");
118
+ return cls ? `${el.tagName.toLowerCase()}.${cls}` : el.tagName.toLowerCase();
119
+ };
120
+ let el;
121
+ while ((el = walker.nextNode())) {
122
+ // must have direct text
123
+ const direct = [...el.childNodes].some(
124
+ (n) => n.nodeType === 3 && n.textContent.trim().length,
125
+ );
126
+ if (!direct) continue;
127
+ const cs = getComputedStyle(el);
128
+ if (cs.visibility === "hidden" || cs.display === "none") continue;
129
+ if (parseFloat(cs.opacity) <= 0.01) continue;
130
+ const rect = el.getBoundingClientRect();
131
+ if (rect.width < 8 || rect.height < 8) continue;
132
+ out.push({
133
+ selector: selectorOf(el),
134
+ text: el.textContent.trim().slice(0, 60),
135
+ fg: parseColor(cs.color),
136
+ fontSize: parseFloat(cs.fontSize),
137
+ fontWeight: Number(cs.fontWeight) || 400,
138
+ bbox: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
139
+ });
140
+ }
141
+ return out;
142
+ });
143
+ }
144
+
145
+ // ─── Pixel sampling + WCAG math ──────────────────────────────────────────────
146
+
147
+ async function annotateFrame(pngBuf, elements) {
148
+ const img = sharp(pngBuf);
149
+ const meta = await img.metadata();
150
+ const { width, height } = meta;
151
+ const raw = await img.ensureAlpha().raw().toBuffer();
152
+ const channels = 4;
153
+
154
+ const measured = [];
155
+ for (const el of elements) {
156
+ const bg = sampleRingMedian(raw, width, height, channels, el.bbox);
157
+ const fg = compositeOver(el.fg, bg); // flatten any alpha against measured bg
158
+ const ratio = wcagRatio(fg, bg);
159
+ const large = isLargeText(el.fontSize, el.fontWeight);
160
+ el.bg = bg;
161
+ el.ratio = +ratio.toFixed(2);
162
+ el.wcagAA = large ? ratio >= 3 : ratio >= 4.5;
163
+ el.wcagAALarge = ratio >= 3;
164
+ el.wcagAAA = large ? ratio >= 4.5 : ratio >= 7;
165
+ measured.push(el);
166
+ }
167
+
168
+ // Draw boxes + ratio labels as an SVG overlay (sharp composite).
169
+ const svg = buildOverlaySVG(measured, width, height);
170
+ return await sharp(pngBuf)
171
+ .composite([{ input: Buffer.from(svg), top: 0, left: 0 }])
172
+ .png()
173
+ .toBuffer();
174
+ }
175
+
176
+ function sampleRingMedian(raw, width, height, channels, bbox) {
177
+ // 4-px ring immediately outside the element bbox. Median of each channel.
178
+ const r = [],
179
+ g = [],
180
+ b = [];
181
+ const x0 = Math.max(0, Math.floor(bbox.x) - 4);
182
+ const x1 = Math.min(width - 1, Math.ceil(bbox.x + bbox.w) + 4);
183
+ const y0 = Math.max(0, Math.floor(bbox.y) - 4);
184
+ const y1 = Math.min(height - 1, Math.ceil(bbox.y + bbox.h) + 4);
185
+ const pushPixel = (x, y) => {
186
+ const i = (y * width + x) * channels;
187
+ r.push(raw[i]);
188
+ g.push(raw[i + 1]);
189
+ b.push(raw[i + 2]);
190
+ };
191
+ for (let x = x0; x <= x1; x++) {
192
+ pushPixel(x, y0);
193
+ pushPixel(x, y1);
194
+ }
195
+ for (let y = y0; y <= y1; y++) {
196
+ pushPixel(x0, y);
197
+ pushPixel(x1, y);
198
+ }
199
+ return [median(r), median(g), median(b), 1];
200
+ }
201
+
202
+ function median(arr) {
203
+ const s = [...arr].sort((a, b) => a - b);
204
+ return s[Math.floor(s.length / 2)];
205
+ }
206
+
207
+ function compositeOver([fr, fg, fb, fa], [br, bg, bb]) {
208
+ return [
209
+ Math.round(fr * fa + br * (1 - fa)),
210
+ Math.round(fg * fa + bg * (1 - fa)),
211
+ Math.round(fb * fa + bb * (1 - fa)),
212
+ 1,
213
+ ];
214
+ }
215
+
216
+ function relLum([r, g, b]) {
217
+ const ch = (v) => {
218
+ const s = v / 255;
219
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
220
+ };
221
+ return 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b);
222
+ }
223
+
224
+ function wcagRatio(a, b) {
225
+ const la = relLum(a);
226
+ const lb = relLum(b);
227
+ const [L1, L2] = la > lb ? [la, lb] : [lb, la];
228
+ return (L1 + 0.05) / (L2 + 0.05);
229
+ }
230
+
231
+ function isLargeText(fontSize, fontWeight) {
232
+ return fontSize >= 24 || (fontSize >= 19 && fontWeight >= 700);
233
+ }
234
+
235
+ // ─── Overlay rendering ───────────────────────────────────────────────────────
236
+
237
+ function buildOverlaySVG(elements, w, h) {
238
+ const rects = elements
239
+ .map((el) => {
240
+ const color = !el.wcagAA ? "#ff00aa" : !el.wcagAAA ? "#ffcc00" : "#00e08a";
241
+ const { x, y, w: bw, h: bh } = el.bbox;
242
+ return `
243
+ <rect x="${x}" y="${y}" width="${bw}" height="${bh}"
244
+ fill="none" stroke="${color}" stroke-width="3"/>
245
+ <rect x="${x}" y="${y - 18}" width="${48}" height="16" fill="${color}"/>
246
+ <text x="${x + 4}" y="${y - 5}" font-family="monospace" font-size="12" fill="#000">
247
+ ${el.ratio.toFixed(1)}:1
248
+ </text>`;
249
+ })
250
+ .join("");
251
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}">${rects}</svg>`;
252
+ }
253
+
254
+ async function writeOverlaySprite(frames, outPath) {
255
+ if (!frames.length) return;
256
+ const cols = Math.min(frames.length, 5);
257
+ const rows = Math.ceil(frames.length / cols);
258
+ const { width, height } = await sharp(frames[0].png).metadata();
259
+ const scale = 0.25;
260
+ const cellW = Math.round(width * scale);
261
+ const cellH = Math.round(height * scale);
262
+
263
+ const cells = await Promise.all(
264
+ frames.map(async (f) => ({
265
+ input: await sharp(f.png).resize(cellW, cellH).png().toBuffer(),
266
+ time: f.t,
267
+ })),
268
+ );
269
+
270
+ const composites = cells.map((c, i) => ({
271
+ input: c.input,
272
+ top: Math.floor(i / cols) * cellH,
273
+ left: (i % cols) * cellW,
274
+ }));
275
+
276
+ await sharp({
277
+ create: {
278
+ width: cols * cellW,
279
+ height: rows * cellH,
280
+ channels: 3,
281
+ background: { r: 16, g: 16, b: 20 },
282
+ },
283
+ })
284
+ .composite(composites)
285
+ .png()
286
+ .toFile(outPath);
287
+ }
288
+
289
+ // ─── Summary ────────────────────────────────────────────────────────────────
290
+
291
+ function summarize(entries) {
292
+ const total = entries.length;
293
+ const failAA = entries.filter((e) => !e.wcagAA).length;
294
+ const passAAonly = entries.filter((e) => e.wcagAA && !e.wcagAAA).length;
295
+ const passAAA = entries.filter((e) => e.wcagAAA).length;
296
+ return { total, failAA, passAAonly, passAAA };
297
+ }
298
+
299
+ function printSummary({ summary, entries }) {
300
+ const { total, failAA, passAAonly, passAAA } = summary;
301
+ console.log(`\nContrast report: ${total} text-element samples`);
302
+ console.log(` fail WCAG AA: ${failAA}`);
303
+ console.log(` pass AA, not AAA: ${passAAonly}`);
304
+ console.log(` pass AAA: ${passAAA}`);
305
+ if (failAA) {
306
+ console.log("\nFailures:");
307
+ for (const e of entries.filter((x) => !x.wcagAA)) {
308
+ console.log(` t=${e.time}s ${e.selector.padEnd(24)} ${e.ratio.toFixed(2)}:1 "${e.text}"`);
309
+ }
310
+ }
311
+ }
312
+
313
+ // ─── Utilities ──────────────────────────────────────────────────────────────
314
+
315
+ function parseArgs(argv) {
316
+ const out = {};
317
+ let positional = 0;
318
+ for (let i = 0; i < argv.length; i++) {
319
+ const a = argv[i];
320
+ if (a.startsWith("--")) {
321
+ const k = a.slice(2);
322
+ const v = argv[i + 1]?.startsWith("--") ? true : argv[++i];
323
+ out[k] = v;
324
+ } else if (positional === 0) {
325
+ out.composition = a;
326
+ positional++;
327
+ }
328
+ }
329
+ return out;
330
+ }
331
+
332
+ function die(msg) {
333
+ console.error(`contrast-report: ${msg}`);
334
+ process.exit(2);
335
+ }
@@ -0,0 +1,211 @@
1
+ # Visual Style Library
2
+
3
+ Named visual identities for HyperFrames videos. Each style is grounded in a real graphic design tradition. Use them to give your video a specific visual personality, not just generic "clean" or "bold."
4
+
5
+ **How to pick:** Match mood first, content second. Ask: _"What should the viewer FEEL?"_
6
+
7
+ **How to use:** Reference the style in your scene plan. Translate the style's principles into concrete composition decisions — palette choice, font selection, entrance patterns, transition type, ambient motion feel.
8
+
9
+ ## Quick Reference
10
+
11
+ | Style | Mood | Best for | Primary shader |
12
+ | --------------- | --------------------- | ---------------------------------- | --------------------------------- |
13
+ | Swiss Pulse | Clinical, precise | SaaS, data, dev tools, metrics | Cinematic Zoom or SDF Iris |
14
+ | Velvet Standard | Premium, timeless | Luxury, enterprise, keynotes | Cross-Warp Morph |
15
+ | Deconstructed | Industrial, raw | Tech launches, security, punk | Glitch or Whip Pan |
16
+ | Maximalist Type | Loud, kinetic | Big announcements, launches | Ridged Burn |
17
+ | Data Drift | Futuristic, immersive | AI, ML, cutting-edge tech | Gravitational Lens or Domain Warp |
18
+ | Soft Signal | Intimate, warm | Wellness, personal stories, brand | Thermal Distortion |
19
+ | Folk Frequency | Cultural, vivid | Consumer apps, food, communities | Swirl Vortex or Ripple Waves |
20
+ | Shadow Cut | Dark, cinematic | Dramatic reveals, security, exposé | Domain Warp |
21
+
22
+ ---
23
+
24
+ ## 1. Swiss Pulse — Josef Müller-Brockmann
25
+
26
+ **Mood:** Clinical, precise | **Best for:** SaaS dashboards, developer tools, APIs, metrics
27
+
28
+ - Black (`#1a1a1a`), white, ONE accent — electric blue (`#0066FF`) or amber (`#FFB300`)
29
+ - Helvetica or Inter Bold for headlines, Regular for labels. Numbers large (80–120px)
30
+ - Grid-locked compositions. Every element snaps to an invisible 12-column grid
31
+ - Animated counters count up from 0. Hard cuts, no decorative transitions
32
+ - Transitions: Cinematic Zoom or SDF Iris (precise, geometric)
33
+
34
+ **GSAP signature:** `expo.out`, `power4.out`. Entries are fast and snap into place. Nothing floats.
35
+
36
+ ```
37
+ Swiss Pulse: Black/white + one electric accent. Grid-locked compositions.
38
+ Numbers dominate the frame at 80-120px. Counter animations from 0.
39
+ Hard cuts or geometric transitions. Nothing decorative.
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 2. Velvet Standard — Massimo Vignelli
45
+
46
+ **Mood:** Premium, timeless | **Best for:** Luxury products, enterprise software, keynotes, investor decks
47
+
48
+ - Black, white, ONE rich accent — deep navy (`#1a237e`) or gold (`#c9a84c`)
49
+ - Thin sans-serif, ALL CAPS, wide letter-spacing (`0.15em+`)
50
+ - Generous negative space. Symmetrical, centered, architectural precision
51
+ - Slow, deliberate. Sequential reveals with long holds. No frantic motion
52
+ - Transitions: Cross-Warp Morph (elegant, organic flow between scenes)
53
+
54
+ **GSAP signature:** `sine.inOut`, `power1`. Nothing snaps — everything glides with intention.
55
+
56
+ ```
57
+ Velvet Standard: Black, white, one rich accent. Thin ALL CAPS type with wide tracking.
58
+ Generous negative space. Sequential reveals, long holds.
59
+ Cross-Warp Morph transitions. Slow and deliberate — luxury takes its time.
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 3. Deconstructed — Neville Brody
65
+
66
+ **Mood:** Industrial, raw | **Best for:** Tech news, developer launches, security products, punk-energy reveals
67
+
68
+ - Dark grey (`#1a1a1a`), rust orange (`#D4501E`), raw white (`#f0f0f0`)
69
+ - Type at angles, overlapping edges, escaping frames. Bold industrial weight
70
+ - Gritty textures: scan-line effects, glitch artifacts baked into the design
71
+ - Text SLAMS and SHATTERS. Letters scramble then snap to final position
72
+ - Transitions: Glitch shader or Whip Pan (breaks the rules, feels aggressive)
73
+
74
+ **GSAP signature:** `back.out(2.5)`, `steps(8)`, `elastic.out(1.2, 0.4)`. Intentional irregularity.
75
+
76
+ ```
77
+ Deconstructed: Dark grey #1a1a1a + rust orange #D4501E. Type at angles, escaping frames.
78
+ Scan-line glitch overlays. Text SLAMS and scrambles into place.
79
+ Glitch shader transitions. Industrial and raw — nothing should feel polished.
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 4. Maximalist Type — Paula Scher
85
+
86
+ **Mood:** Loud, kinetic | **Best for:** Big product launches, milestone announcements, high-energy hype videos
87
+
88
+ - Bold saturated: red (`#E63946`), yellow (`#FFD60A`), black, white — maximum contrast
89
+ - Text IS the visual. Overlapping type layers at different scales and angles, filling 50–80% of frame
90
+ - Everything is kinetic: slamming, sliding, scaling. 2–3 second rapid-fire scenes
91
+ - Text layered OVER footage — never empty backgrounds
92
+ - Transitions: Ridged Burn (explosive, dramatic, impossible to ignore)
93
+
94
+ **GSAP signature:** `expo.out`, `back.out(1.8)`. Fast arrivals, hard stops.
95
+
96
+ ```
97
+ Maximalist Type: Red, yellow, black, white — max contrast. Text IS the visual.
98
+ Overlapping at different scales, 50-80% of frame. Everything in motion.
99
+ Ridged Burn transitions. No static moments — kinetic energy throughout.
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 5. Data Drift — Refik Anadol
105
+
106
+ **Mood:** Futuristic, immersive | **Best for:** AI products, ML platforms, data companies, speculative tech
107
+
108
+ - Iridescent: deep black (`#0a0a0a`), electric purple (`#7c3aed`), cyan (`#06b6d4`)
109
+ - Thin futuristic sans-serif — floating, weightless, minimal
110
+ - Fluid morphing compositions. Extreme scale shifts (micro → macro)
111
+ - Particles coalesce into numbers. Light traces data paths through the frame
112
+ - Transitions: Gravitational Lens or Domain Warp (otherworldly distortion)
113
+
114
+ **GSAP signature:** `sine.inOut`, `power2.out`. Smooth, continuous, organic. Nothing hard.
115
+
116
+ ```
117
+ Data Drift: Deep black #0a0a0a with electric purple #7c3aed and cyan #06b6d4.
118
+ Thin futuristic type, minimal text. Particles coalesce into numbers.
119
+ Gravitational Lens or Domain Warp transitions. Fluid, immersive, otherworldly.
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 6. Soft Signal — Stefan Sagmeister
125
+
126
+ **Mood:** Intimate, warm | **Best for:** Wellness brands, personal stories, lifestyle products, human-centered apps
127
+
128
+ - Warm amber (`#F5A623`), cream (`#FFF8EC`), dusty rose (`#C4A3A3`), sage green (`#8FAF8C`)
129
+ - Handwritten-style or humanist serif fonts. Personal, lowercase, delicate
130
+ - Close-up framing feel: single element fills the frame. Nothing feels corporate
131
+ - Slow drifts and floats, never snaps. Soft organic motion throughout
132
+ - Transitions: Thermal Distortion (warm, flowing, like heat shimmer)
133
+
134
+ **GSAP signature:** `sine.inOut`, `power1.inOut`. Everything breathes.
135
+
136
+ ```
137
+ Soft Signal: Warm amber, cream, dusty rose, sage green. Humanist or handwritten type.
138
+ Single elements fill the frame — intimate, never corporate.
139
+ Slow drifts and floats throughout. Thermal Distortion transitions.
140
+ Nothing should feel hurried or polished.
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 7. Folk Frequency — Eduardo Terrazas
146
+
147
+ **Mood:** Cultural, vivid | **Best for:** Consumer apps, food platforms, community products, festive launches
148
+
149
+ - Vivid folk: hot pink (`#FF1493`), cobalt blue (`#0047AB`), sun yellow (`#FFE000`), emerald (`#009B77`)
150
+ - Bold warm rounded type. Pattern and repetition — folk art rhythm and density
151
+ - Layered compositions with rich visual texture. Every frame feels handcrafted
152
+ - Colorful motion: elements bounce, pop, and spin into place with joy
153
+ - Transitions: Swirl Vortex or Ripple Waves (hypnotic, celebratory)
154
+
155
+ **GSAP signature:** `back.out(1.6)`, `elastic.out(1, 0.5)`. Overshoots feel intentional.
156
+
157
+ ```
158
+ Folk Frequency: Hot pink #FF1493, cobalt blue, sun yellow, emerald. Bold rounded type.
159
+ Pattern and repetition throughout. Layered, dense, handcrafted feeling.
160
+ Swirl Vortex or Ripple Waves transitions. Joyful, celebratory energy.
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 8. Shadow Cut — Hans Hillmann
166
+
167
+ **Mood:** Dark, cinematic | **Best for:** Security products, dramatic reveals, investigative content, intense launches
168
+
169
+ - Near-monochrome: deep blacks (`#0a0a0a`), cold greys (`#3a3a3a`), stark white + blood red (`#C1121F`) or toxic green (`#39FF14`)
170
+ - Sharp angular text like film noir title cards. Heavy contrast, no softness
171
+ - Heavy shadow — elements emerge from darkness. Reveal is the narrative
172
+ - Slow creeping push-ins, dramatic scale reveals, silence before the hit
173
+ - Transitions: Domain Warp (dissolves reality itself before revealing the next scene)
174
+
175
+ **GSAP signature:** `power4.in` for exits, `power3.out` for dramatic reveals. The pause before the hit matters.
176
+
177
+ ```
178
+ Shadow Cut: Deep blacks #0a0a0a, cold greys, stark white + one accent (blood red or toxic green).
179
+ Sharp angular type, film noir aesthetic. Elements emerge from darkness.
180
+ Slow creeping push-ins. Domain Warp transitions. The reveal IS the story.
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Mood → Style Guide
186
+
187
+ | If the content feels... | Use... |
188
+ | ---------------------------------- | --------------- |
189
+ | Data-driven, analytical, technical | Swiss Pulse |
190
+ | Premium, enterprise, luxury | Velvet Standard |
191
+ | Raw, punk, aggressive, rebellious | Deconstructed |
192
+ | Hype, loud, high-energy launch | Maximalist Type |
193
+ | AI, ML, speculative, futuristic | Data Drift |
194
+ | Human, warm, personal, wellness | Soft Signal |
195
+ | Cultural, fun, consumer, festive | Folk Frequency |
196
+ | Dark, dramatic, intense, cinematic | Shadow Cut |
197
+
198
+ ---
199
+
200
+ ## Creating Custom Styles
201
+
202
+ These 8 styles are examples — not constraints. Create your own by:
203
+
204
+ 1. **Name it** after a designer, art movement, or cultural reference
205
+ 2. **Palette**: 2-3 colors max. Declare explicit hex values
206
+ 3. **Typography**: One family, two weights. State the role of each
207
+ 4. **Motion rules**: How fast? Snappy or fluid? Overshoot or precision?
208
+ 5. **Transition**: Which shader matches the energy?
209
+ 6. **What NOT to do**: 2-3 explicit anti-patterns for this style
210
+
211
+ The pattern: **named style → palette → typography → motion rules → transition → avoids.**