use-scroll-animate 6.1.1 → 6.2.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 (104) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/chunks/{base-Yno7N2eC.js → base-C_3cAoRz.js} +2 -2
  3. package/dist/chunks/{base-Yno7N2eC.js.map → base-C_3cAoRz.js.map} +1 -1
  4. package/dist/chunks/{core-CUGCsMsJ.js → core-VEBw36QK.js} +2 -2
  5. package/dist/chunks/{core-CUGCsMsJ.js.map → core-VEBw36QK.js.map} +1 -1
  6. package/dist/chunks/{core-BIOaYdgN.js → core-ps8uvYuN.js} +2 -2
  7. package/dist/chunks/{core-BIOaYdgN.js.map → core-ps8uvYuN.js.map} +1 -1
  8. package/dist/chunks/{fx-BqDeFywO.js → fx-Bs6IrV4Q.js} +2 -2
  9. package/dist/chunks/{fx-BqDeFywO.js.map → fx-Bs6IrV4Q.js.map} +1 -1
  10. package/dist/chunks/generative-D2YyhaeO.js +336 -0
  11. package/dist/chunks/generative-D2YyhaeO.js.map +1 -0
  12. package/dist/chunks/generative-DzIZq-_g.cjs +348 -0
  13. package/dist/chunks/generative-DzIZq-_g.cjs.map +1 -0
  14. package/dist/chunks/gpu-Bu7qwugO.js +380 -0
  15. package/dist/chunks/gpu-Bu7qwugO.js.map +1 -0
  16. package/dist/chunks/gpu-CenK2l6b.cjs +387 -0
  17. package/dist/chunks/gpu-CenK2l6b.cjs.map +1 -0
  18. package/dist/chunks/registry-CKNLQpwd.js +121 -0
  19. package/dist/chunks/registry-CKNLQpwd.js.map +1 -0
  20. package/dist/chunks/registry-DehBVRDV.cjs +131 -0
  21. package/dist/chunks/registry-DehBVRDV.cjs.map +1 -0
  22. package/dist/chunks/{spring-DfQO5USi.js → spring-CbvHfVtP.js} +2 -2
  23. package/dist/chunks/{spring-DfQO5USi.js.map → spring-CbvHfVtP.js.map} +1 -1
  24. package/dist/chunks/{variants-VNscPeTD.js → variants-DRlKVHvu.js} +2 -2
  25. package/dist/chunks/{variants-VNscPeTD.js.map → variants-DRlKVHvu.js.map} +1 -1
  26. package/dist/components/a11y.js +2 -2
  27. package/dist/components/angular.cjs +1 -0
  28. package/dist/components/angular.cjs.map +1 -1
  29. package/dist/components/angular.js +7 -6
  30. package/dist/components/angular.js.map +1 -1
  31. package/dist/components/background.js +2 -2
  32. package/dist/components/bridge.js +1 -1
  33. package/dist/components/cards.js +2 -2
  34. package/dist/components/click.js +3 -3
  35. package/dist/components/depth.js +3 -3
  36. package/dist/components/effects.cjs +71 -403
  37. package/dist/components/effects.cjs.map +1 -1
  38. package/dist/components/effects.js +8 -339
  39. package/dist/components/effects.js.map +1 -1
  40. package/dist/components/feedback.js +1 -1
  41. package/dist/components/fx-gpu.cjs +12 -0
  42. package/dist/components/fx-gpu.cjs.map +1 -0
  43. package/dist/components/fx-gpu.d.cts +63 -0
  44. package/dist/components/fx-gpu.d.ts +63 -0
  45. package/dist/components/fx-gpu.js +5 -0
  46. package/dist/components/fx-gpu.js.map +1 -0
  47. package/dist/components/fx.cjs +13 -133
  48. package/dist/components/fx.cjs.map +1 -1
  49. package/dist/components/fx.js +6 -125
  50. package/dist/components/fx.js.map +1 -1
  51. package/dist/components/fx2.cjs +25 -0
  52. package/dist/components/fx2.cjs.map +1 -0
  53. package/dist/components/fx2.d.cts +136 -0
  54. package/dist/components/fx2.d.ts +136 -0
  55. package/dist/components/fx2.js +17 -0
  56. package/dist/components/fx2.js.map +1 -0
  57. package/dist/components/gesture.js +4 -4
  58. package/dist/components/interaction.js +1 -1
  59. package/dist/components/layout.js +1 -1
  60. package/dist/components/lite.cjs +5 -9
  61. package/dist/components/lite.cjs.map +1 -1
  62. package/dist/components/lite.js +5 -9
  63. package/dist/components/lite.js.map +1 -1
  64. package/dist/components/packs.js +1 -1
  65. package/dist/components/page.js +3 -3
  66. package/dist/components/perf.js +2 -2
  67. package/dist/components/physics.js +3 -3
  68. package/dist/components/reveal.js +1 -1
  69. package/dist/components/solid.cjs +1 -0
  70. package/dist/components/solid.cjs.map +1 -1
  71. package/dist/components/solid.js +7 -6
  72. package/dist/components/solid.js.map +1 -1
  73. package/dist/components/svelte.cjs +1 -0
  74. package/dist/components/svelte.cjs.map +1 -1
  75. package/dist/components/svelte.js +7 -6
  76. package/dist/components/svelte.js.map +1 -1
  77. package/dist/components/svg.js +1 -1
  78. package/dist/components/text.js +2 -2
  79. package/dist/components/timeline.js +3 -3
  80. package/dist/components/transitions.js +1 -1
  81. package/dist/components/ui.js +4 -4
  82. package/dist/components/vue.cjs +1 -0
  83. package/dist/components/vue.cjs.map +1 -1
  84. package/dist/components/vue.js +7 -6
  85. package/dist/components/vue.js.map +1 -1
  86. package/dist/components/webgl.js +1 -1
  87. package/dist/components/widgets.cjs +591 -0
  88. package/dist/components/widgets.cjs.map +1 -0
  89. package/dist/components/widgets.d.cts +110 -0
  90. package/dist/components/widgets.d.ts +110 -0
  91. package/dist/components/widgets.js +581 -0
  92. package/dist/components/widgets.js.map +1 -0
  93. package/dist/components.cjs +10 -9
  94. package/dist/components.cjs.map +1 -1
  95. package/dist/components.js +10 -9
  96. package/dist/components.js.map +1 -1
  97. package/dist/components.umd.js +2 -2
  98. package/dist/components.umd.js.map +1 -1
  99. package/dist/index.js +2 -2
  100. package/dist/widgets.umd.js +2 -0
  101. package/dist/widgets.umd.js.map +1 -0
  102. package/docs/ROADMAP.md +10 -11
  103. package/docs/components.md +29 -0
  104. package/package.json +31 -1
@@ -1,59 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  var components_fx = require('./fx.cjs');
4
+ var generative = require('../chunks/generative-DzIZq-_g.cjs');
4
5
  var base = require('../chunks/base-BaQV-2ha.cjs');
6
+ var registry = require('../chunks/registry-DehBVRDV.cjs');
5
7
  var components_tokens = require('./tokens.cjs');
6
8
  var core = require('../chunks/core-BGAyaY6L.cjs');
7
9
  require('../chunks/fx-lBGVtQO1.cjs');
8
10
 
9
- let layer = null;
10
- /** A fixed, pointer-transparent, aria-hidden layer for transient particles. */
11
- function fxLayer() {
12
- if (layer?.isConnected)
13
- return layer;
14
- layer = document.createElement('div');
15
- layer.setAttribute('aria-hidden', 'true');
16
- layer.setAttribute('data-usa-fx-layer', '');
17
- layer.style.cssText = 'position:fixed;inset:0;pointer-events:none;overflow:hidden;z-index:2147483000;contain:strict';
18
- document.body.appendChild(layer);
19
- return layer;
20
- }
21
- /** Client point of the triggering pointer event, or the element's center. */
22
- function origin(el, ctx) {
23
- const e = ctx.event;
24
- const r = el.getBoundingClientRect();
25
- return e && typeof e.clientX === 'number' && (e.clientX || e.clientY) ? { x: e.clientX, y: e.clientY } : { x: r.left + r.width / 2, y: r.top + r.height / 2 };
26
- }
27
- /** Spawn an absolutely positioned node in the fx layer at (x, y); removed when its animation ends. */
28
- function spawn(x, y, css, ctx, frames, opts, text = '') {
29
- const n = document.createElement('span');
30
- n.style.cssText = `position:absolute;left:${x}px;top:${y}px;${css}`;
31
- if (text)
32
- n.textContent = text;
33
- fxLayer().appendChild(n);
34
- const a = ctx.animate(n, frames, { fill: 'forwards', ...opts });
35
- const rm = () => n.remove();
36
- if (a)
37
- a.finished.then(rm, rm);
38
- else
39
- rm();
40
- return a;
41
- }
42
- /** An overlay child covering `el` (makes `el` a positioning context). Returns it and a remover. */
43
- function overlay(el, css) {
44
- const o = document.createElement('span');
45
- o.setAttribute('aria-hidden', 'true');
46
- o.style.cssText = `position:absolute;inset:0;pointer-events:none;border-radius:inherit;${css}`;
47
- if (getComputedStyle(el).position === 'static')
48
- el.style.position = 'relative';
49
- el.appendChild(o);
50
- return [o, () => o.remove()];
51
- }
52
- const PALETTE = ['#7c5cff', '#ff5c8a', '#22d3ee', '#facc15', '#34d399'];
53
- const rand = (a, b) => a + Math.random() * (b - a);
54
- /** Wait for all animations (ignoring nulls). */
55
- const all = (anims) => Promise.all(anims.filter(Boolean).map((a) => a.finished.catch(() => undefined)));
56
-
57
11
  const CARD_FX = [
58
12
  {
59
13
  name: 'holo',
@@ -62,7 +16,7 @@ const CARD_FX = [
62
16
  reduced: 'run',
63
17
  defaults: { strength: 0.55 },
64
18
  run: (el, o, ctx) => {
65
- const [layer, remove] = overlay(el, `mix-blend-mode:color-dodge;opacity:${o.strength};background:linear-gradient(115deg,transparent 20%,#ff8bd855 35%,#8bf3ff55 45%,#fff58b55 55%,transparent 70%),repeating-linear-gradient(55deg,#ffffff10 0 2px,transparent 2px 6px);background-size:250% 250%,100% 100%;background-position:50% 50%;transition:background-position .2s ease-out`);
19
+ const [layer, remove] = generative.overlay(el, `mix-blend-mode:color-dodge;opacity:${o.strength};background:linear-gradient(115deg,transparent 20%,#ff8bd855 35%,#8bf3ff55 45%,#fff58b55 55%,transparent 70%),repeating-linear-gradient(55deg,#ffffff10 0 2px,transparent 2px 6px);background-size:250% 250%,100% 100%;background-position:50% 50%;transition:background-position .2s ease-out`);
66
20
  if (ctx.reduced)
67
21
  return remove;
68
22
  const move = (e) => {
@@ -94,7 +48,7 @@ const CARD_FX = [
94
48
  run: (el, o, ctx) => {
95
49
  if (ctx.reduced)
96
50
  return;
97
- const [g, remove] = overlay(el, `overflow:hidden;background:linear-gradient(105deg,transparent 35%,${o.color} 50%,transparent 65%);background-size:250% 100%`);
51
+ const [g, remove] = generative.overlay(el, `overflow:hidden;background:linear-gradient(105deg,transparent 35%,${o.color} 50%,transparent 65%);background-size:250% 100%`);
98
52
  const a = ctx.animate(g, [{ backgroundPosition: '150% 0' }, { backgroundPosition: '-50% 0' }], { duration: o.duration, easing: 'ease-in-out' });
99
53
  if (a)
100
54
  a.finished.then(remove, remove);
@@ -121,7 +75,7 @@ const CARD_FX = [
121
75
  run: (el, o, ctx) => {
122
76
  const kids = Array.from(el.children);
123
77
  const mid = (kids.length - 1) / 2;
124
- return all(kids.map((k, i) => ctx.animate(k, [{ transform: 'none' }, { transform: `translateX(${(i - mid) * o.spread}px) rotate(${(i - mid) * (o.spread / 2)}deg)`, offset: 0.5 }, { transform: 'none' }], { duration: o.duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' })));
78
+ return generative.all(kids.map((k, i) => ctx.animate(k, [{ transform: 'none' }, { transform: `translateX(${(i - mid) * o.spread}px) rotate(${(i - mid) * (o.spread / 2)}deg)`, offset: 0.5 }, { transform: 'none' }], { duration: o.duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' })));
125
79
  },
126
80
  },
127
81
  {
@@ -154,24 +108,24 @@ const CLICK_FX = [
154
108
  run: (el, o, ctx) => {
155
109
  if (ctx.reduced)
156
110
  return;
157
- const { x, y } = origin(el, ctx);
158
- return all([0, 120].map((delay) => spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:3px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(0)', opacity: 0.9 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' })));
111
+ const { x, y } = generative.origin(el, ctx);
112
+ return generative.all([0, 120].map((delay) => generative.spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:3px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(0)', opacity: 0.9 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' })));
159
113
  },
160
114
  },
161
115
  {
162
116
  name: 'ink-splash',
163
117
  kind: 'click',
164
118
  description: 'Ink blobs splatter from the click point and fade.',
165
- defaults: { count: 9, colors: PALETTE, duration: 800 },
119
+ defaults: { count: 9, colors: generative.PALETTE, duration: 800 },
166
120
  run: (el, o, ctx) => {
167
121
  if (ctx.reduced)
168
122
  return;
169
- const { x, y } = origin(el, ctx);
170
- return all(Array.from({ length: o.count }, (_, i) => {
171
- const s = rand(6, 22);
172
- const ang = rand(0, Math.PI * 2);
173
- const d = rand(20, 70);
174
- return spawn(x, y, `width:${s}px;height:${s}px;margin:${-s / 2}px 0 0 ${-s / 2}px;background:${o.colors[i % o.colors.length]};border-radius:${rand(40, 50)}% ${rand(50, 60)}% ${rand(40, 60)}% ${rand(45, 55)}%`, ctx, [{ transform: 'translate(0,0) scale(.3)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) scale(1)`, opacity: 0.9, offset: 0.5 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d + 8}px) scale(1.1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' });
123
+ const { x, y } = generative.origin(el, ctx);
124
+ return generative.all(Array.from({ length: o.count }, (_, i) => {
125
+ const s = generative.rand(6, 22);
126
+ const ang = generative.rand(0, Math.PI * 2);
127
+ const d = generative.rand(20, 70);
128
+ return generative.spawn(x, y, `width:${s}px;height:${s}px;margin:${-s / 2}px 0 0 ${-s / 2}px;background:${o.colors[i % o.colors.length]};border-radius:${generative.rand(40, 50)}% ${generative.rand(50, 60)}% ${generative.rand(40, 60)}% ${generative.rand(45, 55)}%`, ctx, [{ transform: 'translate(0,0) scale(.3)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) scale(1)`, opacity: 0.9, offset: 0.5 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d + 8}px) scale(1.1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' });
175
129
  }));
176
130
  },
177
131
  },
@@ -183,12 +137,12 @@ const CLICK_FX = [
183
137
  run: (el, o, ctx) => {
184
138
  if (ctx.reduced)
185
139
  return;
186
- const { x, y } = origin(el, ctx);
187
- return all(Array.from({ length: o.count }, (_, i) => {
140
+ const { x, y } = generative.origin(el, ctx);
141
+ return generative.all(Array.from({ length: o.count }, (_, i) => {
188
142
  const ang = (i / o.count) * Math.PI * 2;
189
- const d = rand(40, 80);
190
- const s = rand(12, 22);
191
- return spawn(x, y, `font-size:${s}px;line-height:1;margin:${-s / 2}px 0 0 ${-s / 2}px;color:${o.colors[i % o.colors.length]}`, ctx, [{ transform: 'translate(0,0) rotate(0) scale(.2)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) rotate(${rand(180, 360)}deg) scale(1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' }, '★');
143
+ const d = generative.rand(40, 80);
144
+ const s = generative.rand(12, 22);
145
+ return generative.spawn(x, y, `font-size:${s}px;line-height:1;margin:${-s / 2}px 0 0 ${-s / 2}px;color:${o.colors[i % o.colors.length]}`, ctx, [{ transform: 'translate(0,0) rotate(0) scale(.2)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) rotate(${generative.rand(180, 360)}deg) scale(1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' }, '★');
192
146
  }));
193
147
  },
194
148
  },
@@ -207,8 +161,8 @@ const CLICK_FX = [
207
161
  run: (el, o, ctx) => {
208
162
  if (ctx.reduced)
209
163
  return;
210
- const { x, y } = origin(el, ctx);
211
- return all(Array.from({ length: o.rings }, (_, i) => spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:2px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(.1)', opacity: 0.8 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay: i * 160, easing: 'ease-out' })));
164
+ const { x, y } = generative.origin(el, ctx);
165
+ return generative.all(Array.from({ length: o.rings }, (_, i) => generative.spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:2px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(.1)', opacity: 0.8 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay: i * 160, easing: 'ease-out' })));
212
166
  },
213
167
  },
214
168
  {
@@ -219,11 +173,11 @@ const CLICK_FX = [
219
173
  run: (el, o, ctx) => {
220
174
  if (ctx.reduced)
221
175
  return;
222
- const { x, y } = origin(el, ctx);
176
+ const { x, y } = generative.origin(el, ctx);
223
177
  const chars = Array.from(String(o.emoji));
224
- return all(Array.from({ length: o.count }, (_, i) => {
225
- const dx = rand(-90, 90);
226
- return spawn(x, y, `font-size:${rand(16, 28)}px;line-height:1`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${dx * 0.6}px,${rand(-90, -50)}px) scale(1)`, opacity: 1, offset: 0.35 }, { transform: `translate(${dx}px,${rand(40, 90)}px) rotate(${rand(-60, 60)}deg) scale(.9)`, opacity: 0 }], { duration: o.duration, delay: i * 25, easing: 'cubic-bezier(0.33, 0, 0.67, 1)' }, chars[i % chars.length]);
178
+ return generative.all(Array.from({ length: o.count }, (_, i) => {
179
+ const dx = generative.rand(-90, 90);
180
+ return generative.spawn(x, y, `font-size:${generative.rand(16, 28)}px;line-height:1`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${dx * 0.6}px,${generative.rand(-90, -50)}px) scale(1)`, opacity: 1, offset: 0.35 }, { transform: `translate(${dx}px,${generative.rand(40, 90)}px) rotate(${generative.rand(-60, 60)}deg) scale(.9)`, opacity: 0 }], { duration: o.duration, delay: i * 25, easing: 'cubic-bezier(0.33, 0, 0.67, 1)' }, chars[i % chars.length]);
227
181
  }));
228
182
  },
229
183
  },
@@ -370,7 +324,7 @@ const PHYSICS_FX = [
370
324
  el.dataset.usaSplit = '1';
371
325
  }
372
326
  const hs = bounceKeyframes(o.bounce, 32);
373
- return all(Array.from(el.children).map((s, i) => ctx.animate(s, hs.map((h) => ({ transform: `translateY(${(-h * o.height).toFixed(1)}px)` })), { duration: o.duration, delay: i * o.stagger, easing: 'linear', fill: 'backwards' })));
327
+ return generative.all(Array.from(el.children).map((s, i) => ctx.animate(s, hs.map((h) => ({ transform: `translateY(${(-h * o.height).toFixed(1)}px)` })), { duration: o.duration, delay: i * o.stagger, easing: 'linear', fill: 'backwards' })));
374
328
  },
375
329
  },
376
330
  {
@@ -504,7 +458,7 @@ const PAGE_FX = [
504
458
  reduced: 'run',
505
459
  defaults: TRANSITION_DEFAULTS,
506
460
  run: transition((s, o, ctx, el) => {
507
- const { x, y } = origin(el, ctx);
461
+ const { x, y } = generative.origin(el, ctx);
508
462
  const r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)) + 2;
509
463
  // a huge box-shadow ring around a hole: the hole shrinks to 0 to cover
510
464
  const p = part(s, `left:${x}px;top:${y}px;width:0;height:0;border-radius:50%;box-shadow:0 0 0 ${Math.ceil(r)}px ${o.color}`);
@@ -829,292 +783,6 @@ function defineStory(tag = 'usa-story') {
829
783
  }, { id: 'usa-story', text: CSS });
830
784
  }
831
785
 
832
- /** Deterministic smooth pseudo-noise in [-1, 1] (sum of sines — cheap, no tables). */
833
- function noise2(x, y, t = 0) {
834
- return (Math.sin(x * 1.7 + t) * Math.cos(y * 1.3 - t * 0.7) + Math.sin((x + y) * 0.9 + t * 0.5) * 0.5 + Math.cos(x * 0.6 - y * 1.1 - t * 0.3) * 0.5) / 2;
835
- }
836
- /** Hex `#rrggbb` → [r, g, b]. */
837
- function hexRgb(hex) {
838
- const m = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(hex.trim());
839
- return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : [124, 92, 255];
840
- }
841
- /**
842
- * Mount a generative canvas behind `el` and run `spec` on it. Returns the
843
- * cleanup. Exposed for custom generative effects.
844
- */
845
- function canvasBackground(el, fx, spec, o) {
846
- const canvas = document.createElement('canvas');
847
- canvas.setAttribute('aria-hidden', 'true');
848
- canvas.setAttribute('data-usa-fx-canvas', '');
849
- canvas.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:-1;border-radius:inherit';
850
- const restore = [];
851
- const set = (k, v) => {
852
- restore.push([k, el.style[k]]);
853
- el.style[k] = v;
854
- };
855
- if (getComputedStyle(el).position === 'static')
856
- set('position', 'relative');
857
- set('isolation', 'isolate');
858
- el.prepend(canvas);
859
- const ctx = canvas.getContext('2d');
860
- let raf = 0;
861
- let visible = true;
862
- let quality = Math.min(1, Math.max(0.35, Number(o.quality) || 1));
863
- let w = 0;
864
- let h = 0;
865
- let state = null;
866
- const t0 = performance.now();
867
- let last = t0;
868
- let slow = 0;
869
- let fast = 0;
870
- const resize = () => {
871
- const r = el.getBoundingClientRect();
872
- w = Math.max(1, Math.round(r.width));
873
- h = Math.max(1, Math.round(r.height));
874
- const dpr = Math.min(2, (typeof devicePixelRatio === 'number' ? devicePixelRatio : 1) || 1) * quality;
875
- canvas.width = Math.max(1, Math.round(w * dpr));
876
- canvas.height = Math.max(1, Math.round(h * dpr));
877
- if (ctx)
878
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
879
- state = spec.init ? spec.init(w, h, o) : {};
880
- };
881
- const frame = (now) => {
882
- raf = 0;
883
- if (!ctx)
884
- return;
885
- const dt = now - last;
886
- last = now;
887
- // adaptive quality: sustained slow frames lower the render scale, fast ones raise it
888
- if (dt > 28)
889
- slow++;
890
- else if (dt < 18)
891
- fast++;
892
- if (slow > 20 && quality > 0.35) {
893
- quality = Math.max(0.35, quality - 0.15);
894
- slow = fast = 0;
895
- resize();
896
- }
897
- else if (fast > 240 && quality < 1) {
898
- quality = Math.min(1, quality + 0.15);
899
- slow = fast = 0;
900
- resize();
901
- }
902
- spec.draw({ ctx, w, h, t: (now - t0) / 1000, quality, state, o });
903
- if (visible && !document.hidden)
904
- raf = requestAnimationFrame(frame);
905
- };
906
- const start = () => {
907
- if (!raf && ctx && !fx.reduced) {
908
- last = performance.now();
909
- raf = requestAnimationFrame(frame);
910
- }
911
- };
912
- resize();
913
- if (ctx)
914
- spec.draw({ ctx, w, h, t: 0, quality, state, o }); // first / static frame
915
- const io = typeof IntersectionObserver === 'function' ? new IntersectionObserver((es) => {
916
- visible = es.some((e) => e.isIntersecting);
917
- if (visible)
918
- start();
919
- }) : null;
920
- io?.observe(el);
921
- const ro = typeof ResizeObserver === 'function' ? new ResizeObserver(() => {
922
- resize();
923
- if (ctx && fx.reduced)
924
- spec.draw({ ctx, w, h, t: 0, quality, state, o });
925
- }) : null;
926
- ro?.observe(el);
927
- const vis = () => !document.hidden && visible && start();
928
- document.addEventListener('visibilitychange', vis);
929
- start();
930
- return () => {
931
- cancelAnimationFrame(raf);
932
- raf = 0;
933
- io?.disconnect();
934
- ro?.disconnect();
935
- document.removeEventListener('visibilitychange', vis);
936
- canvas.remove();
937
- for (const [k, v] of restore)
938
- el.style[k] = v;
939
- };
940
- }
941
- const gen = (name, description, defaults, spec) => ({
942
- name,
943
- kind: 'background',
944
- description,
945
- reduced: 'run',
946
- defaults: { colors: PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
947
- run: (el, o, ctx) => canvasBackground(el, ctx, spec, o),
948
- });
949
- /** Low-resolution scalar field → ImageData, drawn scaled up (used by voronoi / metaballs / contours). */
950
- function field(f, cell, px) {
951
- const step = Math.max(2, Math.round(cell / f.quality));
952
- const cw = Math.max(1, Math.ceil(f.w / step));
953
- const ch = Math.max(1, Math.ceil(f.h / step));
954
- const st = f.state;
955
- if (!st.buf || st.buf.width !== cw || st.buf.height !== ch) {
956
- st.buf = document.createElement('canvas');
957
- st.buf.width = cw;
958
- st.buf.height = ch;
959
- st.bctx = st.buf.getContext('2d');
960
- st.img = st.bctx?.createImageData(cw, ch);
961
- }
962
- if (!st.bctx || !st.img)
963
- return;
964
- const d = st.img.data;
965
- for (let y = 0; y < ch; y++)
966
- for (let x = 0; x < cw; x++)
967
- px(x * step, y * step, d, (y * cw + x) * 4);
968
- st.bctx.putImageData(st.img, 0, 0);
969
- f.ctx.imageSmoothingEnabled = true;
970
- f.ctx.drawImage(st.buf, 0, 0, f.w, f.h);
971
- }
972
- const GENERATIVE_FX = [
973
- gen('flow-field', 'Particles drift along a slowly changing flow field, leaving silky trails.', { count: 600, fade: 0.06 }, {
974
- init: (w, h, o) => ({ p: Array.from({ length: o.count }, () => [Math.random() * w, Math.random() * h]) }),
975
- draw: ({ ctx, w, h, t, state, o, quality }) => {
976
- ctx.fillStyle = o.background;
977
- ctx.globalAlpha = t === 0 ? 1 : o.fade;
978
- ctx.fillRect(0, 0, w, h);
979
- ctx.globalAlpha = 0.8;
980
- const n = Math.round(state.p.length * quality);
981
- const steps = t === 0 ? 40 : 1;
982
- for (let i = 0; i < n; i++) {
983
- const q = state.p[i];
984
- ctx.fillStyle = o.colors[i % o.colors.length];
985
- for (let s = 0; s < steps; s++) {
986
- const a = noise2(q[0] * 0.004, q[1] * 0.004, t * 0.15 * o.speed) * Math.PI * 2;
987
- q[0] += Math.cos(a) * 1.2 * o.speed;
988
- q[1] += Math.sin(a) * 1.2 * o.speed;
989
- if (q[0] < 0 || q[0] > w || q[1] < 0 || q[1] > h)
990
- (q[0] = Math.random() * w), (q[1] = Math.random() * h);
991
- ctx.fillRect(q[0], q[1], 1.4, 1.4);
992
- }
993
- }
994
- ctx.globalAlpha = 1;
995
- },
996
- }),
997
- gen('voronoi', 'Drifting Voronoi cells in the palette, with darker borders.', { seeds: 14, cell: 6 }, {
998
- init: (w, h, o) => ({ s: Array.from({ length: o.seeds }, (_, i) => ({ x: Math.random() * w, y: Math.random() * h, vx: Math.random() - 0.5, vy: Math.random() - 0.5, c: hexRgb(o.colors[i % o.colors.length]) })) }),
999
- draw: (f) => {
1000
- const { w, h, state, o } = f;
1001
- for (const s of state.s) {
1002
- s.x += s.vx * o.speed;
1003
- s.y += s.vy * o.speed;
1004
- if (s.x < 0 || s.x > w)
1005
- s.vx *= -1;
1006
- if (s.y < 0 || s.y > h)
1007
- s.vy *= -1;
1008
- }
1009
- field(f, o.cell, (x, y, d, i) => {
1010
- let b = Infinity;
1011
- let b2 = Infinity;
1012
- let c = state.s[0].c;
1013
- for (const s of state.s) {
1014
- const dd = (s.x - x) ** 2 + (s.y - y) ** 2;
1015
- if (dd < b)
1016
- (b2 = b), (b = dd), (c = s.c);
1017
- else if (dd < b2)
1018
- b2 = dd;
1019
- }
1020
- const edge = Math.sqrt(b2) - Math.sqrt(b) < 6 ? 0.55 : 1;
1021
- d[i] = c[0] * edge;
1022
- d[i + 1] = c[1] * edge;
1023
- d[i + 2] = c[2] * edge;
1024
- d[i + 3] = 255;
1025
- });
1026
- },
1027
- }),
1028
- gen('mesh-gradient', 'Soft blobs of colour orbit and blend into a living mesh gradient.', { blobs: 4, blur: 0.65 }, {
1029
- draw: ({ ctx, w, h, t, o }) => {
1030
- ctx.fillStyle = o.background;
1031
- ctx.fillRect(0, 0, w, h);
1032
- const r = Math.max(w, h) * o.blur;
1033
- for (let i = 0; i < o.blobs; i++) {
1034
- const a = t * 0.25 * o.speed + (i * Math.PI * 2) / o.blobs;
1035
- const x = w / 2 + Math.cos(a * (1 + i * 0.13)) * w * 0.32;
1036
- const y = h / 2 + Math.sin(a * (0.8 + i * 0.11)) * h * 0.32;
1037
- const [cr, cg, cb] = hexRgb(o.colors[i % o.colors.length]);
1038
- const g = ctx.createRadialGradient(x, y, 0, x, y, r);
1039
- g.addColorStop(0, `rgba(${cr},${cg},${cb},0.7)`);
1040
- g.addColorStop(1, `rgba(${cr},${cg},${cb},0)`);
1041
- ctx.fillStyle = g;
1042
- ctx.fillRect(0, 0, w, h);
1043
- }
1044
- },
1045
- }),
1046
- gen('starfield', 'Stars stream toward the viewer (warp speed with `speed: 4`).', { stars: 400, color: '#ffffff' }, {
1047
- init: (_w, _h, o) => ({ s: Array.from({ length: o.stars }, () => [Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random()]) }),
1048
- draw: ({ ctx, w, h, t, state, o, quality }) => {
1049
- ctx.fillStyle = o.background;
1050
- ctx.fillRect(0, 0, w, h);
1051
- ctx.fillStyle = o.color;
1052
- const n = Math.round(state.s.length * quality);
1053
- for (let i = 0; i < n; i++) {
1054
- const s = state.s[i];
1055
- if (t > 0)
1056
- s[2] -= 0.004 * o.speed;
1057
- if (s[2] <= 0.01)
1058
- (s[0] = Math.random() * 2 - 1), (s[1] = Math.random() * 2 - 1), (s[2] = 1);
1059
- const x = w / 2 + (s[0] / s[2]) * w * 0.5;
1060
- const y = h / 2 + (s[1] / s[2]) * h * 0.5;
1061
- if (x < 0 || x > w || y < 0 || y > h)
1062
- continue;
1063
- const size = (1 - s[2]) * 2.6;
1064
- ctx.globalAlpha = Math.min(1, (1 - s[2]) * 1.6);
1065
- ctx.fillRect(x, y, size, size);
1066
- }
1067
- ctx.globalAlpha = 1;
1068
- },
1069
- }),
1070
- gen('metaballs', 'Gooey blobs that merge and split (low-resolution field, scaled up).', { balls: 6, cell: 5, threshold: 1 }, {
1071
- init: (w, h, o) => ({ b: Array.from({ length: o.balls }, () => ({ x: Math.random() * w, y: Math.random() * h, vx: (Math.random() - 0.5) * 1.6, vy: (Math.random() - 0.5) * 1.6, r: Math.min(w, h) * (0.08 + Math.random() * 0.08) })) }),
1072
- draw: (f) => {
1073
- const { w, h, state, o } = f;
1074
- for (const b of state.b) {
1075
- b.x += b.vx * o.speed;
1076
- b.y += b.vy * o.speed;
1077
- if (b.x < 0 || b.x > w)
1078
- b.vx *= -1;
1079
- if (b.y < 0 || b.y > h)
1080
- b.vy *= -1;
1081
- }
1082
- const [ar, ag, ab] = hexRgb(o.colors[0]);
1083
- const [br, bg, bb] = hexRgb(o.colors[1] || o.colors[0]);
1084
- const [kr, kg, kb] = hexRgb(o.background);
1085
- field(f, o.cell, (x, y, d, i) => {
1086
- let v = 0;
1087
- for (const b of state.b)
1088
- v += (b.r * b.r) / ((b.x - x) ** 2 + (b.y - y) ** 2 + 1);
1089
- const inside = v >= o.threshold;
1090
- const k = Math.min(1, (v - o.threshold) * 0.8);
1091
- d[i] = inside ? ar + (br - ar) * k : kr;
1092
- d[i + 1] = inside ? ag + (bg - ag) * k : kg;
1093
- d[i + 2] = inside ? ab + (bb - ab) * k : kb;
1094
- d[i + 3] = 255;
1095
- });
1096
- },
1097
- }),
1098
- gen('contours', 'Topographic contour lines over a slowly shifting height field.', { levels: 9, cell: 4, color: '#7c5cff' }, {
1099
- draw: (f) => {
1100
- const { t, o } = f;
1101
- const [lr, lg, lb] = hexRgb(o.color);
1102
- const [kr, kg, kb] = hexRgb(o.background);
1103
- const z = (x, y) => (noise2(x * 0.006, y * 0.006, t * 0.2 * o.speed) + 1) / 2;
1104
- const step = Math.max(2, Math.round(o.cell / f.quality));
1105
- field(f, o.cell, (x, y, d, i) => {
1106
- const a = Math.floor(z(x, y) * o.levels);
1107
- const line = a !== Math.floor(z(x + step, y) * o.levels) || a !== Math.floor(z(x, y + step) * o.levels);
1108
- const shade = 0.12 + (a / o.levels) * 0.18;
1109
- d[i] = line ? lr : kr + (lr - kr) * shade;
1110
- d[i + 1] = line ? lg : kg + (lg - kg) * shade;
1111
- d[i + 2] = line ? lb : kb + (lb - kb) * shade;
1112
- d[i + 3] = 255;
1113
- });
1114
- },
1115
- }),
1116
- ];
1117
-
1118
786
  let actx = null;
1119
787
  let current = null;
1120
788
  const mediaSources = new WeakMap();
@@ -1279,7 +947,7 @@ function bindBeat(el, name, options = {}) {
1279
947
  const { threshold, cooldown, history, floor, ...fx } = options;
1280
948
  return onBeat(() => {
1281
949
  if (!base.prefersReducedMotion())
1282
- components_fx.playEffect(el, name, fx).catch(() => undefined);
950
+ registry.playEffect(el, name, fx).catch(() => undefined);
1283
951
  }, { threshold, cooldown, history, floor });
1284
952
  }
1285
953
  // --- visual effects ----------------------------------------------------------
@@ -1300,8 +968,8 @@ const audioFx = (name, description, defaults, spec) => ({
1300
968
  kind: 'background',
1301
969
  description,
1302
970
  reduced: 'skip',
1303
- defaults: { colors: PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
1304
- run: (el, o, ctx) => canvasBackground(el, ctx, spec, o),
971
+ defaults: { colors: generative.PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
972
+ run: (el, o, ctx) => generative.canvasBackground(el, ctx, spec, o),
1305
973
  });
1306
974
  const AUDIO_FX = [
1307
975
  audioFx('spectrum-bars', 'Frequency bars dance to the audio (mirror them with `mirror: true`).', { bars: 48, gap: 2, mirror: false }, {
@@ -1326,7 +994,7 @@ const AUDIO_FX = [
1326
994
  ctx.fillStyle = o.background;
1327
995
  ctx.fillRect(0, 0, w, h);
1328
996
  const s = read(t, 32);
1329
- const [r, g, b] = hexRgb(o.color);
997
+ const [r, g, b] = generative.hexRgb(o.color);
1330
998
  const base = Math.min(w, h) * 0.18;
1331
999
  for (let i = 0; i < o.rings; i++) {
1332
1000
  const rad = base * (1 + i * 0.55) * (1 + s.bass * 0.6);
@@ -1414,7 +1082,7 @@ function defineAudio(tag = 'usa-audio') {
1414
1082
  catch {
1415
1083
  /* ignore bad JSON */
1416
1084
  }
1417
- components_fx.playEffect(el, el.dataset.usaBeat || 'pulse', opts).catch(() => undefined);
1085
+ registry.playEffect(el, el.dataset.usaBeat || 'pulse', opts).catch(() => undefined);
1418
1086
  });
1419
1087
  }, { threshold: this.num('threshold', 1.35), cooldown: this.num('cooldown', 250) });
1420
1088
  }
@@ -1446,7 +1114,7 @@ const touchOk = (e, o) => o.touch || e.pointerType !== 'touch';
1446
1114
  function overlayCanvas(draw) {
1447
1115
  const c = document.createElement('canvas');
1448
1116
  c.style.cssText = 'position:absolute;inset:0;width:100%;height:100%';
1449
- fxLayer().appendChild(c);
1117
+ generative.fxLayer().appendChild(c);
1450
1118
  const g = c.getContext('2d');
1451
1119
  let raf = 0;
1452
1120
  const frame = (now) => {
@@ -1508,7 +1176,7 @@ const CURSOR_FX = [
1508
1176
  description: 'A glowing comet tail follows the pointer over the element (persistent; `color`, `width`, `life`).',
1509
1177
  defaults: { color: '#7c5cff', width: 10, life: 320, touch: false },
1510
1178
  run: (el, o) => trail(el, o, (g, pts, now) => {
1511
- const [r, gg, b] = hexRgb(o.color);
1179
+ const [r, gg, b] = generative.hexRgb(o.color);
1512
1180
  g.lineCap = 'round';
1513
1181
  for (let i = 1; i < pts.length; i++) {
1514
1182
  const k = 1 - (now - pts[i].t) / o.life;
@@ -1546,7 +1214,7 @@ const CURSOR_FX = [
1546
1214
  name: 'sparkle-trail',
1547
1215
  kind: 'cursor',
1548
1216
  description: 'Little stars twinkle off the pointer as it moves (persistent; `colors`, `spacing` px between stars).',
1549
- defaults: { colors: PALETTE, spacing: 14, size: 14, touch: false },
1217
+ defaults: { colors: generative.PALETTE, spacing: 14, size: 14, touch: false },
1550
1218
  run: (el, o, ctx) => {
1551
1219
  let lx = -1e4;
1552
1220
  let ly = -1e4;
@@ -1555,8 +1223,8 @@ const CURSOR_FX = [
1555
1223
  return;
1556
1224
  lx = e.clientX;
1557
1225
  ly = e.clientY;
1558
- const s = o.size * rand(0.6, 1.2);
1559
- spawn(e.clientX - s / 2, e.clientY - s / 2, `font-size:${s}px;line-height:1;color:${o.colors[Math.floor(rand(0, o.colors.length))]}`, ctx, [{ transform: 'translate(0,0) scale(0) rotate(0deg)', opacity: 1 }, { transform: `translate(${rand(-14, 14)}px,${rand(4, 26)}px) scale(1) rotate(${rand(-90, 90)}deg)`, opacity: 0 }], { duration: rand(500, 800), easing: 'ease-out' }, '✦');
1226
+ const s = o.size * generative.rand(0.6, 1.2);
1227
+ generative.spawn(e.clientX - s / 2, e.clientY - s / 2, `font-size:${s}px;line-height:1;color:${o.colors[Math.floor(generative.rand(0, o.colors.length))]}`, ctx, [{ transform: 'translate(0,0) scale(0) rotate(0deg)', opacity: 1 }, { transform: `translate(${generative.rand(-14, 14)}px,${generative.rand(4, 26)}px) scale(1) rotate(${generative.rand(-90, 90)}deg)`, opacity: 0 }], { duration: generative.rand(500, 800), easing: 'ease-out' }, '✦');
1560
1228
  };
1561
1229
  el.addEventListener('pointermove', move, { passive: true });
1562
1230
  return () => el.removeEventListener('pointermove', move);
@@ -1580,7 +1248,7 @@ const CURSOR_FX = [
1580
1248
  const leave = () => ((p.x = -1e4), (p.y = -1e4));
1581
1249
  el.addEventListener('pointermove', move, { passive: true });
1582
1250
  el.addEventListener('pointerleave', leave);
1583
- const off = canvasBackground(el, ctx, {
1251
+ const off = generative.canvasBackground(el, ctx, {
1584
1252
  draw: ({ ctx: g, w, h }) => {
1585
1253
  g.clearRect(0, 0, w, h);
1586
1254
  if (o.background !== 'transparent') {
@@ -1614,7 +1282,7 @@ const CURSOR_FX = [
1614
1282
  description: 'A soft light follows the pointer across the element, easing behind it (persistent; `color`, `size`).',
1615
1283
  defaults: { color: '#ffffff', size: 260, ease: 0.18, touch: false },
1616
1284
  run: (el, o) => {
1617
- const [r, g, b] = hexRgb(o.color);
1285
+ const [r, g, b] = generative.hexRgb(o.color);
1618
1286
  const s = document.createElement('span');
1619
1287
  s.setAttribute('aria-hidden', 'true');
1620
1288
  s.style.cssText = `position:absolute;inset:0;pointer-events:none;border-radius:inherit;opacity:0;transition:opacity .25s;mix-blend-mode:soft-light;background:radial-gradient(circle ${o.size / 2}px at var(--sx,50%) var(--sy,50%),rgba(${r},${g},${b},.75),transparent)`;
@@ -1700,7 +1368,7 @@ function bindGesture(el, gesture, effect, o = {}) {
1700
1368
  if (typeof effect === 'function')
1701
1369
  effect(d, e);
1702
1370
  else
1703
- components_fx.playEffect(el, effect, effectOptions, e).catch(() => undefined);
1371
+ registry.playEffect(el, effect, effectOptions, e).catch(() => undefined);
1704
1372
  };
1705
1373
  const twistAngle = () => {
1706
1374
  const [a, b] = [...pts.values()];
@@ -1814,7 +1482,7 @@ function defineGestureFx(tag = 'usa-gesture-fx') {
1814
1482
  }
1815
1483
  const target = this.flag('self') ? this : this.firstElementChild || this;
1816
1484
  const effect = this.str('effect', 'pulse');
1817
- this.onCleanup(bindGesture(this, this.gesture, (_d, e) => void components_fx.playEffect(target, effect, effectOptions, e).catch(() => undefined), {
1485
+ this.onCleanup(bindGesture(this, this.gesture, (_d, e) => void registry.playEffect(target, effect, effectOptions, e).catch(() => undefined), {
1818
1486
  velocity: this.num('velocity', 0.8),
1819
1487
  angle: this.num('angle', 30),
1820
1488
  duration: this.num('duration', 650),
@@ -1855,11 +1523,11 @@ function bumpCount(el, delta, ctx) {
1855
1523
  const pop = (el, ctx, s = 1.25, d = 380) => ctx.animate(el, [{ transform: 'scale(1)' }, { transform: `scale(${s})`, offset: 0.4 }, { transform: 'scale(1)' }], { duration: d, easing: 'cubic-bezier(.2,1.4,.4,1)' });
1856
1524
  /** A few glyphs flying out of the pointer / centre. */
1857
1525
  function burst(el, ctx, glyph, color, n = 6, rise = false) {
1858
- const { x, y } = origin(el, ctx);
1859
- return all(Array.from({ length: n }, (_, i) => {
1860
- const a = rise ? -Math.PI / 2 + rand(-0.6, 0.6) : (i / n) * Math.PI * 2;
1861
- const d = rand(28, 56);
1862
- return spawn(x - 7, y - 7, `font-size:14px;line-height:1;color:${color}`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 1 }, { transform: `translate(${Math.cos(a) * d}px,${Math.sin(a) * d}px) scale(1)`, opacity: 0 }], { duration: rand(500, 750), easing: 'cubic-bezier(.2,.8,.3,1)' }, glyph);
1526
+ const { x, y } = generative.origin(el, ctx);
1527
+ return generative.all(Array.from({ length: n }, (_, i) => {
1528
+ const a = rise ? -Math.PI / 2 + generative.rand(-0.6, 0.6) : (i / n) * Math.PI * 2;
1529
+ const d = generative.rand(28, 56);
1530
+ return generative.spawn(x - 7, y - 7, `font-size:14px;line-height:1;color:${color}`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 1 }, { transform: `translate(${Math.cos(a) * d}px,${Math.sin(a) * d}px) scale(1)`, opacity: 0 }], { duration: generative.rand(500, 750), easing: 'cubic-bezier(.2,.8,.3,1)' }, glyph);
1863
1531
  }));
1864
1532
  }
1865
1533
  const click = (name, description, defaults, run) => ({ name, kind: 'click', description, defaults, run });
@@ -1902,7 +1570,7 @@ const MICRO_FX = [
1902
1570
  if (el.getAttribute('aria-busy') === 'true')
1903
1571
  return;
1904
1572
  el.setAttribute('aria-busy', 'true');
1905
- const [bar, remove] = overlay(el, `background:${o.color}55;transform-origin:left;transform:scaleX(0)`);
1573
+ const [bar, remove] = generative.overlay(el, `background:${o.color}55;transform-origin:left;transform:scaleX(0)`);
1906
1574
  const a = ctx.animate(bar, [{ transform: 'scaleX(0)' }, { transform: 'scaleX(1)' }], { duration: o.duration, easing: 'linear', fill: 'forwards' });
1907
1575
  const done = a ? a.finished.catch(() => undefined) : Promise.resolve();
1908
1576
  return done.then(() => {
@@ -1928,12 +1596,12 @@ const MICRO_FX = [
1928
1596
  click('send-plane', 'A paper plane ✈ takes off from the button.', { color: '#22d3ee' }, (el, o, ctx) => {
1929
1597
  const r = el.getBoundingClientRect();
1930
1598
  pop(el, ctx, 0.92, 200);
1931
- return spawn(r.left + r.width / 2 - 8, r.top + r.height / 2 - 8, `font-size:16px;color:${o.color}`, ctx, [{ transform: 'translate(0,0) rotate(0)', opacity: 1 }, { transform: 'translate(40px,-10px) rotate(-10deg)', opacity: 1, offset: 0.4 }, { transform: 'translate(160px,-90px) rotate(-25deg)', opacity: 0 }], { duration: 800, easing: 'ease-in' }, '✈')?.finished;
1599
+ return generative.spawn(r.left + r.width / 2 - 8, r.top + r.height / 2 - 8, `font-size:16px;color:${o.color}`, ctx, [{ transform: 'translate(0,0) rotate(0)', opacity: 1 }, { transform: 'translate(40px,-10px) rotate(-10deg)', opacity: 1, offset: 0.4 }, { transform: 'translate(160px,-90px) rotate(-25deg)', opacity: 0 }], { duration: 800, easing: 'ease-in' }, '✈')?.finished;
1932
1600
  }),
1933
1601
  click('add-to-cart', 'Bumps `[data-count]` by one and floats a "+1" (`text`).', { text: '+1', color: '#34d399' }, (el, o, ctx) => {
1934
1602
  bumpCount(el, 1, ctx);
1935
- const { x, y } = origin(el, ctx);
1936
- return spawn(x - 10, y - 10, `font:700 14px system-ui;color:${o.color}`, ctx, [{ transform: 'translateY(0)', opacity: 1 }, { transform: 'translateY(-40px)', opacity: 0 }], { duration: 700, easing: 'ease-out' }, o.text)?.finished;
1603
+ const { x, y } = generative.origin(el, ctx);
1604
+ return generative.spawn(x - 10, y - 10, `font:700 14px system-ui;color:${o.color}`, ctx, [{ transform: 'translateY(0)', opacity: 1 }, { transform: 'translateY(-40px)', opacity: 0 }], { duration: 700, easing: 'ease-out' }, o.text)?.finished;
1937
1605
  }),
1938
1606
  click('counter-bump', 'Adds `step` to `[data-count]` with a rolling number.', { step: 1 }, (el, o, ctx) => void bumpCount(el, Number(o.step) || 1, ctx)),
1939
1607
  click('upvote', 'Toggles an upvote: the arrow nudges up and `[data-count]` ±1.', {}, (el, _o, ctx) => {
@@ -1967,7 +1635,7 @@ const MICRO_FX = [
1967
1635
  }),
1968
1636
  attn('error-flash', 'Flashes the element red once (no more than one flash per call).', { color: '#ff5c8a' }, (el, o, ctx) => ctx.animate(el, [{ boxShadow: `0 0 0 0 ${o.color}00` }, { boxShadow: `0 0 0 4px ${o.color}`, offset: 0.3 }, { boxShadow: `0 0 0 0 ${o.color}00` }], { duration: 700 })),
1969
1637
  attn('success-check', 'A green ✓ badge pops over the element and fades.', { color: '#34d399' }, (el, o, ctx) => {
1970
- const [b, remove] = overlay(el, `display:grid;place-items:center;font:700 22px system-ui;color:#fff;background:${o.color}d0`);
1638
+ const [b, remove] = generative.overlay(el, `display:grid;place-items:center;font:700 22px system-ui;color:#fff;background:${o.color}d0`);
1971
1639
  b.textContent = '✓';
1972
1640
  const a = ctx.animate(b, [{ opacity: 0, transform: 'scale(.6)' }, { opacity: 1, transform: 'scale(1)', offset: 0.3 }, { opacity: 1, offset: 0.75 }, { opacity: 0 }], { duration: 1100, easing: 'ease-out' });
1973
1641
  return a ? a.finished.then(remove, remove) : void setTimeout(remove, 900);
@@ -2082,7 +1750,7 @@ function themePreset(t, role) {
2082
1750
  function playThemeEffect(el, role, theme) {
2083
1751
  const name = theme || el.closest('[data-usa-theme]')?.getAttribute('data-usa-theme') || 'neon';
2084
1752
  const pr = themePreset(name, role);
2085
- return components_fx.playEffect(el, pr.effect, pr.options);
1753
+ return registry.playEffect(el, pr.effect, pr.options);
2086
1754
  }
2087
1755
  const THEME_FX = [
2088
1756
  {
@@ -2105,7 +1773,7 @@ const THEME_FX = [
2105
1773
  description: 'A bright diagonal shine sweeps across frosted glass.',
2106
1774
  defaults: { duration: 700 },
2107
1775
  run: (el, o, ctx) => {
2108
- const [s, remove] = overlay(el, 'overflow:hidden;background:linear-gradient(105deg,transparent 35%,#ffffff8c 50%,transparent 65%);background-size:250% 100%;background-position:120% 0');
1776
+ const [s, remove] = generative.overlay(el, 'overflow:hidden;background:linear-gradient(105deg,transparent 35%,#ffffff8c 50%,transparent 65%);background-size:250% 100%;background-position:120% 0');
2109
1777
  const a = ctx.animate(s, [{ backgroundPosition: '120% 0' }, { backgroundPosition: '-20% 0' }], { duration: o.duration, easing: 'ease-in-out' });
2110
1778
  return a ? a.finished.then(remove, remove) : void remove();
2111
1779
  },
@@ -2117,7 +1785,7 @@ const THEME_FX = [
2117
1785
  reduced: 'run',
2118
1786
  defaults: { opacity: 0.18 },
2119
1787
  run: (el, o, ctx) => {
2120
- const [s, remove] = overlay(el, `opacity:${o.opacity};background:repeating-linear-gradient(0deg,#000 0 1px,transparent 1px 3px);mix-blend-mode:multiply`);
1788
+ const [s, remove] = generative.overlay(el, `opacity:${o.opacity};background:repeating-linear-gradient(0deg,#000 0 1px,transparent 1px 3px);mix-blend-mode:multiply`);
2121
1789
  const a = ctx.reduced ? null : ctx.animate(s, [{ backgroundPosition: '0 0' }, { backgroundPosition: '0 30px' }], { duration: 2400, iterations: Infinity, easing: 'linear' });
2122
1790
  return () => {
2123
1791
  a?.cancel();
@@ -2152,7 +1820,7 @@ function defineTheme(tag = 'usa-theme') {
2152
1820
  const pr = themePreset(this.theme, role);
2153
1821
  const trigger = role === 'attention' ? 'click' : role === 'background' ? 'load' : role;
2154
1822
  try {
2155
- this.onCleanup(components_fx.bindEffect(el, pr.effect, { ...(pr.options || {}), trigger }));
1823
+ this.onCleanup(registry.bindEffect(el, pr.effect, { ...(pr.options || {}), trigger }));
2156
1824
  }
2157
1825
  catch {
2158
1826
  /* effect not registered: call registerAllEffects() */
@@ -2271,7 +1939,7 @@ function createPlayer(root, animation, o = {}) {
2271
1939
  if (!e.fired && time >= e.t.start) {
2272
1940
  e.fired = true;
2273
1941
  for (const el of e.els)
2274
- components_fx.playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
1942
+ registry.playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
2275
1943
  }
2276
1944
  };
2277
1945
  const done = () => {
@@ -2479,7 +2147,7 @@ const EFFECT_PACKS = {
2479
2147
  'cards-click': [...CARD_FX, ...CLICK_FX],
2480
2148
  physics: PHYSICS_FX,
2481
2149
  page: PAGE_FX,
2482
- generative: GENERATIVE_FX,
2150
+ generative: generative.GENERATIVE_FX,
2483
2151
  audio: AUDIO_FX,
2484
2152
  cursor: CURSOR_FX,
2485
2153
  micro: MICRO_FX,
@@ -2487,32 +2155,32 @@ const EFFECT_PACKS = {
2487
2155
  };
2488
2156
  /** 5.1: card & click effects 2.0. */
2489
2157
  function registerCardClickEffects() {
2490
- components_fx.registerEffects(EFFECT_PACKS['cards-click']);
2158
+ registry.registerEffects(EFFECT_PACKS['cards-click']);
2491
2159
  }
2492
2160
  /** 5.2: bounce & physics micro-interactions. */
2493
2161
  function registerPhysicsEffects() {
2494
- components_fx.registerEffects(EFFECT_PACKS.physics);
2162
+ registry.registerEffects(EFFECT_PACKS.physics);
2495
2163
  }
2496
2164
  /** 5.3: page-wide transitions and effects. */
2497
2165
  function registerPageEffects() {
2498
- components_fx.registerEffects(EFFECT_PACKS.page);
2166
+ registry.registerEffects(EFFECT_PACKS.page);
2499
2167
  }
2500
2168
  /** 5.5: generative Canvas 2D backgrounds. */
2501
2169
  function registerGenerativeEffects() {
2502
- components_fx.registerEffects(EFFECT_PACKS.generative);
2170
+ registry.registerEffects(EFFECT_PACKS.generative);
2503
2171
  }
2504
2172
  /** 5.6: sound-reactive (Web Audio) backgrounds. */
2505
2173
  function registerAudioEffects() {
2506
- components_fx.registerEffects(EFFECT_PACKS.audio);
2174
+ registry.registerEffects(EFFECT_PACKS.audio);
2507
2175
  }
2508
2176
  /** 5.7: cursor trails, magnetic dots, spotlight cursor. */
2509
2177
  function registerCursorEffects() {
2510
- components_fx.registerEffects(EFFECT_PACKS.cursor);
2178
+ registry.registerEffects(EFFECT_PACKS.cursor);
2511
2179
  }
2512
2180
  /** 5.8: micro-interactions + theme-pack effects. */
2513
2181
  function registerMicroEffects() {
2514
- components_fx.registerEffects(EFFECT_PACKS.micro);
2515
- components_fx.registerEffects(EFFECT_PACKS.themes);
2182
+ registry.registerEffects(EFFECT_PACKS.micro);
2183
+ registry.registerEffects(EFFECT_PACKS.themes);
2516
2184
  }
2517
2185
  /** Define the 5.x elements of this entry (`<usa-story>`, …) under their default tags. */
2518
2186
  function defineEffectElements() {
@@ -2526,16 +2194,20 @@ function defineEffectElements() {
2526
2194
  function registerAllEffects() {
2527
2195
  components_fx.registerBuiltinEffects();
2528
2196
  for (const defs of Object.values(EFFECT_PACKS))
2529
- components_fx.registerEffects(defs);
2197
+ registry.registerEffects(defs);
2530
2198
  }
2531
2199
 
2200
+ exports.GENERATIVE_FX = generative.GENERATIVE_FX;
2201
+ exports.canvasBackground = generative.canvasBackground;
2202
+ exports.fxLayer = generative.fxLayer;
2203
+ exports.hexRgb = generative.hexRgb;
2204
+ exports.noise2 = generative.noise2;
2532
2205
  exports.ANIMATION_FORMAT = ANIMATION_FORMAT;
2533
2206
  exports.AUDIO_FX = AUDIO_FX;
2534
2207
  exports.CARD_FX = CARD_FX;
2535
2208
  exports.CLICK_FX = CLICK_FX;
2536
2209
  exports.CURSOR_FX = CURSOR_FX;
2537
2210
  exports.EFFECT_PACKS = EFFECT_PACKS;
2538
- exports.GENERATIVE_FX = GENERATIVE_FX;
2539
2211
  exports.GESTURES = GESTURES;
2540
2212
  exports.MICRO_FX = MICRO_FX;
2541
2213
  exports.PAGE_FX = PAGE_FX;
@@ -2551,7 +2223,6 @@ exports.bindBeat = bindBeat;
2551
2223
  exports.bindGesture = bindGesture;
2552
2224
  exports.bounceKeyframes = bounceKeyframes;
2553
2225
  exports.bumpCount = bumpCount;
2554
- exports.canvasBackground = canvasBackground;
2555
2226
  exports.createBeatDetector = createBeatDetector;
2556
2227
  exports.createPlayer = createPlayer;
2557
2228
  exports.defineAudio = defineAudio;
@@ -2564,10 +2235,7 @@ exports.disableAudio = disableAudio;
2564
2235
  exports.enableAudio = enableAudio;
2565
2236
  exports.flingVelocity = flingVelocity;
2566
2237
  exports.formatCount = formatCount;
2567
- exports.fxLayer = fxLayer;
2568
2238
  exports.getAudio = getAudio;
2569
- exports.hexRgb = hexRgb;
2570
- exports.noise2 = noise2;
2571
2239
  exports.normalizeAnimation = normalizeAnimation;
2572
2240
  exports.onBeat = onBeat;
2573
2241
  exports.playThemeEffect = playThemeEffect;