use-scroll-animate 6.1.1 → 6.3.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 (114) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/chunks/{base-Yno7N2eC.js → base-DchG4q_S.js} +2 -2
  3. package/dist/chunks/{base-Yno7N2eC.js.map → base-DchG4q_S.js.map} +1 -1
  4. package/dist/chunks/{core-BIOaYdgN.js → core-D2vF8kZb.js} +2 -2
  5. package/dist/chunks/{core-BIOaYdgN.js.map → core-D2vF8kZb.js.map} +1 -1
  6. package/dist/chunks/{core-CUGCsMsJ.js → core-DVyTlo9Q.js} +2 -2
  7. package/dist/chunks/{core-CUGCsMsJ.js.map → core-DVyTlo9Q.js.map} +1 -1
  8. package/dist/chunks/{fx-BqDeFywO.js → fx-DUMVKSvg.js} +2 -2
  9. package/dist/chunks/{fx-BqDeFywO.js.map → fx-DUMVKSvg.js.map} +1 -1
  10. package/dist/chunks/generative-BHIj-NU0.cjs +295 -0
  11. package/dist/chunks/generative-BHIj-NU0.cjs.map +1 -0
  12. package/dist/chunks/generative-BRUZaIsX.js +290 -0
  13. package/dist/chunks/generative-BRUZaIsX.js.map +1 -0
  14. package/dist/chunks/gpu-BBRLgEzM.cjs +388 -0
  15. package/dist/chunks/gpu-BBRLgEzM.cjs.map +1 -0
  16. package/dist/chunks/gpu-XHWzeNb5.js +381 -0
  17. package/dist/chunks/gpu-XHWzeNb5.js.map +1 -0
  18. package/dist/chunks/registry-Bu5NrAyA.js +121 -0
  19. package/dist/chunks/registry-Bu5NrAyA.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/shared-eweTlzxv.js +50 -0
  23. package/dist/chunks/shared-eweTlzxv.js.map +1 -0
  24. package/dist/chunks/shared-jkgRH-Hx.cjs +58 -0
  25. package/dist/chunks/shared-jkgRH-Hx.cjs.map +1 -0
  26. package/dist/chunks/{spring-DfQO5USi.js → spring-BhoT09Qb.js} +2 -2
  27. package/dist/chunks/{spring-DfQO5USi.js.map → spring-BhoT09Qb.js.map} +1 -1
  28. package/dist/chunks/{variants-VNscPeTD.js → variants-C_hv_s23.js} +2 -2
  29. package/dist/chunks/{variants-VNscPeTD.js.map → variants-C_hv_s23.js.map} +1 -1
  30. package/dist/components/a11y.js +2 -2
  31. package/dist/components/angular.cjs +1 -0
  32. package/dist/components/angular.cjs.map +1 -1
  33. package/dist/components/angular.js +7 -6
  34. package/dist/components/angular.js.map +1 -1
  35. package/dist/components/background.js +2 -2
  36. package/dist/components/bridge.js +1 -1
  37. package/dist/components/cards.js +2 -2
  38. package/dist/components/click.js +3 -3
  39. package/dist/components/depth.js +3 -3
  40. package/dist/components/effects.cjs +72 -403
  41. package/dist/components/effects.cjs.map +1 -1
  42. package/dist/components/effects.js +9 -339
  43. package/dist/components/effects.js.map +1 -1
  44. package/dist/components/feedback.js +1 -1
  45. package/dist/components/fx-gpu.cjs +13 -0
  46. package/dist/components/fx-gpu.cjs.map +1 -0
  47. package/dist/components/fx-gpu.d.cts +63 -0
  48. package/dist/components/fx-gpu.d.ts +63 -0
  49. package/dist/components/fx-gpu.js +6 -0
  50. package/dist/components/fx-gpu.js.map +1 -0
  51. package/dist/components/fx-text.cjs +267 -0
  52. package/dist/components/fx-text.cjs.map +1 -0
  53. package/dist/components/fx-text.d.cts +70 -0
  54. package/dist/components/fx-text.d.ts +70 -0
  55. package/dist/components/fx-text.js +263 -0
  56. package/dist/components/fx-text.js.map +1 -0
  57. package/dist/components/fx.cjs +13 -133
  58. package/dist/components/fx.cjs.map +1 -1
  59. package/dist/components/fx.js +6 -125
  60. package/dist/components/fx.js.map +1 -1
  61. package/dist/components/fx2.cjs +32 -0
  62. package/dist/components/fx2.cjs.map +1 -0
  63. package/dist/components/fx2.d.cts +162 -0
  64. package/dist/components/fx2.d.ts +162 -0
  65. package/dist/components/fx2.js +22 -0
  66. package/dist/components/fx2.js.map +1 -0
  67. package/dist/components/gesture.js +4 -4
  68. package/dist/components/interaction.js +1 -1
  69. package/dist/components/layout.js +1 -1
  70. package/dist/components/lite.cjs +5 -9
  71. package/dist/components/lite.cjs.map +1 -1
  72. package/dist/components/lite.js +5 -9
  73. package/dist/components/lite.js.map +1 -1
  74. package/dist/components/packs.js +1 -1
  75. package/dist/components/page.js +3 -3
  76. package/dist/components/perf.js +2 -2
  77. package/dist/components/physics.js +3 -3
  78. package/dist/components/reveal.js +1 -1
  79. package/dist/components/solid.cjs +1 -0
  80. package/dist/components/solid.cjs.map +1 -1
  81. package/dist/components/solid.js +7 -6
  82. package/dist/components/solid.js.map +1 -1
  83. package/dist/components/svelte.cjs +1 -0
  84. package/dist/components/svelte.cjs.map +1 -1
  85. package/dist/components/svelte.js +7 -6
  86. package/dist/components/svelte.js.map +1 -1
  87. package/dist/components/svg.js +1 -1
  88. package/dist/components/text.js +2 -2
  89. package/dist/components/timeline.js +3 -3
  90. package/dist/components/transitions.js +1 -1
  91. package/dist/components/ui.js +4 -4
  92. package/dist/components/vue.cjs +1 -0
  93. package/dist/components/vue.cjs.map +1 -1
  94. package/dist/components/vue.js +7 -6
  95. package/dist/components/vue.js.map +1 -1
  96. package/dist/components/webgl.js +1 -1
  97. package/dist/components/widgets.cjs +1174 -0
  98. package/dist/components/widgets.cjs.map +1 -0
  99. package/dist/components/widgets.d.cts +206 -0
  100. package/dist/components/widgets.d.ts +206 -0
  101. package/dist/components/widgets.js +1155 -0
  102. package/dist/components/widgets.js.map +1 -0
  103. package/dist/components.cjs +10 -9
  104. package/dist/components.cjs.map +1 -1
  105. package/dist/components.js +10 -9
  106. package/dist/components.js.map +1 -1
  107. package/dist/components.umd.js +2 -2
  108. package/dist/components.umd.js.map +1 -1
  109. package/dist/index.js +2 -2
  110. package/dist/widgets.umd.js +2 -0
  111. package/dist/widgets.umd.js.map +1 -0
  112. package/docs/ROADMAP.md +10 -11
  113. package/docs/components.md +61 -0
  114. package/package.json +41 -1
@@ -1,59 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var components_fx = require('./fx.cjs');
4
+ var shared = require('../chunks/shared-jkgRH-Hx.cjs');
4
5
  var base = require('../chunks/base-BaQV-2ha.cjs');
6
+ var generative = require('../chunks/generative-BHIj-NU0.cjs');
7
+ var registry = require('../chunks/registry-DehBVRDV.cjs');
5
8
  var components_tokens = require('./tokens.cjs');
6
9
  var core = require('../chunks/core-BGAyaY6L.cjs');
7
10
  require('../chunks/fx-lBGVtQO1.cjs');
8
11
 
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
12
  const CARD_FX = [
58
13
  {
59
14
  name: 'holo',
@@ -62,7 +17,7 @@ const CARD_FX = [
62
17
  reduced: 'run',
63
18
  defaults: { strength: 0.55 },
64
19
  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`);
20
+ const [layer, remove] = shared.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
21
  if (ctx.reduced)
67
22
  return remove;
68
23
  const move = (e) => {
@@ -94,7 +49,7 @@ const CARD_FX = [
94
49
  run: (el, o, ctx) => {
95
50
  if (ctx.reduced)
96
51
  return;
97
- const [g, remove] = overlay(el, `overflow:hidden;background:linear-gradient(105deg,transparent 35%,${o.color} 50%,transparent 65%);background-size:250% 100%`);
52
+ const [g, remove] = shared.overlay(el, `overflow:hidden;background:linear-gradient(105deg,transparent 35%,${o.color} 50%,transparent 65%);background-size:250% 100%`);
98
53
  const a = ctx.animate(g, [{ backgroundPosition: '150% 0' }, { backgroundPosition: '-50% 0' }], { duration: o.duration, easing: 'ease-in-out' });
99
54
  if (a)
100
55
  a.finished.then(remove, remove);
@@ -121,7 +76,7 @@ const CARD_FX = [
121
76
  run: (el, o, ctx) => {
122
77
  const kids = Array.from(el.children);
123
78
  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)' })));
79
+ return shared.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
80
  },
126
81
  },
127
82
  {
@@ -154,24 +109,24 @@ const CLICK_FX = [
154
109
  run: (el, o, ctx) => {
155
110
  if (ctx.reduced)
156
111
  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)' })));
112
+ const { x, y } = shared.origin(el, ctx);
113
+ return shared.all([0, 120].map((delay) => shared.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
114
  },
160
115
  },
161
116
  {
162
117
  name: 'ink-splash',
163
118
  kind: 'click',
164
119
  description: 'Ink blobs splatter from the click point and fade.',
165
- defaults: { count: 9, colors: PALETTE, duration: 800 },
120
+ defaults: { count: 9, colors: shared.PALETTE, duration: 800 },
166
121
  run: (el, o, ctx) => {
167
122
  if (ctx.reduced)
168
123
  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)' });
124
+ const { x, y } = shared.origin(el, ctx);
125
+ return shared.all(Array.from({ length: o.count }, (_, i) => {
126
+ const s = shared.rand(6, 22);
127
+ const ang = shared.rand(0, Math.PI * 2);
128
+ const d = shared.rand(20, 70);
129
+ return shared.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:${shared.rand(40, 50)}% ${shared.rand(50, 60)}% ${shared.rand(40, 60)}% ${shared.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
130
  }));
176
131
  },
177
132
  },
@@ -183,12 +138,12 @@ const CLICK_FX = [
183
138
  run: (el, o, ctx) => {
184
139
  if (ctx.reduced)
185
140
  return;
186
- const { x, y } = origin(el, ctx);
187
- return all(Array.from({ length: o.count }, (_, i) => {
141
+ const { x, y } = shared.origin(el, ctx);
142
+ return shared.all(Array.from({ length: o.count }, (_, i) => {
188
143
  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)' }, '★');
144
+ const d = shared.rand(40, 80);
145
+ const s = shared.rand(12, 22);
146
+ return shared.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(${shared.rand(180, 360)}deg) scale(1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' }, '★');
192
147
  }));
193
148
  },
194
149
  },
@@ -207,8 +162,8 @@ const CLICK_FX = [
207
162
  run: (el, o, ctx) => {
208
163
  if (ctx.reduced)
209
164
  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' })));
165
+ const { x, y } = shared.origin(el, ctx);
166
+ return shared.all(Array.from({ length: o.rings }, (_, i) => shared.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
167
  },
213
168
  },
214
169
  {
@@ -219,11 +174,11 @@ const CLICK_FX = [
219
174
  run: (el, o, ctx) => {
220
175
  if (ctx.reduced)
221
176
  return;
222
- const { x, y } = origin(el, ctx);
177
+ const { x, y } = shared.origin(el, ctx);
223
178
  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]);
179
+ return shared.all(Array.from({ length: o.count }, (_, i) => {
180
+ const dx = shared.rand(-90, 90);
181
+ return shared.spawn(x, y, `font-size:${shared.rand(16, 28)}px;line-height:1`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${dx * 0.6}px,${shared.rand(-90, -50)}px) scale(1)`, opacity: 1, offset: 0.35 }, { transform: `translate(${dx}px,${shared.rand(40, 90)}px) rotate(${shared.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
182
  }));
228
183
  },
229
184
  },
@@ -370,7 +325,7 @@ const PHYSICS_FX = [
370
325
  el.dataset.usaSplit = '1';
371
326
  }
372
327
  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' })));
328
+ return shared.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
329
  },
375
330
  },
376
331
  {
@@ -504,7 +459,7 @@ const PAGE_FX = [
504
459
  reduced: 'run',
505
460
  defaults: TRANSITION_DEFAULTS,
506
461
  run: transition((s, o, ctx, el) => {
507
- const { x, y } = origin(el, ctx);
462
+ const { x, y } = shared.origin(el, ctx);
508
463
  const r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)) + 2;
509
464
  // a huge box-shadow ring around a hole: the hole shrinks to 0 to cover
510
465
  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 +784,6 @@ function defineStory(tag = 'usa-story') {
829
784
  }, { id: 'usa-story', text: CSS });
830
785
  }
831
786
 
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
787
  let actx = null;
1119
788
  let current = null;
1120
789
  const mediaSources = new WeakMap();
@@ -1279,7 +948,7 @@ function bindBeat(el, name, options = {}) {
1279
948
  const { threshold, cooldown, history, floor, ...fx } = options;
1280
949
  return onBeat(() => {
1281
950
  if (!base.prefersReducedMotion())
1282
- components_fx.playEffect(el, name, fx).catch(() => undefined);
951
+ registry.playEffect(el, name, fx).catch(() => undefined);
1283
952
  }, { threshold, cooldown, history, floor });
1284
953
  }
1285
954
  // --- visual effects ----------------------------------------------------------
@@ -1300,8 +969,8 @@ const audioFx = (name, description, defaults, spec) => ({
1300
969
  kind: 'background',
1301
970
  description,
1302
971
  reduced: 'skip',
1303
- defaults: { colors: PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
1304
- run: (el, o, ctx) => canvasBackground(el, ctx, spec, o),
972
+ defaults: { colors: shared.PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
973
+ run: (el, o, ctx) => generative.canvasBackground(el, ctx, spec, o),
1305
974
  });
1306
975
  const AUDIO_FX = [
1307
976
  audioFx('spectrum-bars', 'Frequency bars dance to the audio (mirror them with `mirror: true`).', { bars: 48, gap: 2, mirror: false }, {
@@ -1326,7 +995,7 @@ const AUDIO_FX = [
1326
995
  ctx.fillStyle = o.background;
1327
996
  ctx.fillRect(0, 0, w, h);
1328
997
  const s = read(t, 32);
1329
- const [r, g, b] = hexRgb(o.color);
998
+ const [r, g, b] = generative.hexRgb(o.color);
1330
999
  const base = Math.min(w, h) * 0.18;
1331
1000
  for (let i = 0; i < o.rings; i++) {
1332
1001
  const rad = base * (1 + i * 0.55) * (1 + s.bass * 0.6);
@@ -1414,7 +1083,7 @@ function defineAudio(tag = 'usa-audio') {
1414
1083
  catch {
1415
1084
  /* ignore bad JSON */
1416
1085
  }
1417
- components_fx.playEffect(el, el.dataset.usaBeat || 'pulse', opts).catch(() => undefined);
1086
+ registry.playEffect(el, el.dataset.usaBeat || 'pulse', opts).catch(() => undefined);
1418
1087
  });
1419
1088
  }, { threshold: this.num('threshold', 1.35), cooldown: this.num('cooldown', 250) });
1420
1089
  }
@@ -1446,7 +1115,7 @@ const touchOk = (e, o) => o.touch || e.pointerType !== 'touch';
1446
1115
  function overlayCanvas(draw) {
1447
1116
  const c = document.createElement('canvas');
1448
1117
  c.style.cssText = 'position:absolute;inset:0;width:100%;height:100%';
1449
- fxLayer().appendChild(c);
1118
+ shared.fxLayer().appendChild(c);
1450
1119
  const g = c.getContext('2d');
1451
1120
  let raf = 0;
1452
1121
  const frame = (now) => {
@@ -1508,7 +1177,7 @@ const CURSOR_FX = [
1508
1177
  description: 'A glowing comet tail follows the pointer over the element (persistent; `color`, `width`, `life`).',
1509
1178
  defaults: { color: '#7c5cff', width: 10, life: 320, touch: false },
1510
1179
  run: (el, o) => trail(el, o, (g, pts, now) => {
1511
- const [r, gg, b] = hexRgb(o.color);
1180
+ const [r, gg, b] = generative.hexRgb(o.color);
1512
1181
  g.lineCap = 'round';
1513
1182
  for (let i = 1; i < pts.length; i++) {
1514
1183
  const k = 1 - (now - pts[i].t) / o.life;
@@ -1546,7 +1215,7 @@ const CURSOR_FX = [
1546
1215
  name: 'sparkle-trail',
1547
1216
  kind: 'cursor',
1548
1217
  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 },
1218
+ defaults: { colors: shared.PALETTE, spacing: 14, size: 14, touch: false },
1550
1219
  run: (el, o, ctx) => {
1551
1220
  let lx = -1e4;
1552
1221
  let ly = -1e4;
@@ -1555,8 +1224,8 @@ const CURSOR_FX = [
1555
1224
  return;
1556
1225
  lx = e.clientX;
1557
1226
  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' }, '✦');
1227
+ const s = o.size * shared.rand(0.6, 1.2);
1228
+ shared.spawn(e.clientX - s / 2, e.clientY - s / 2, `font-size:${s}px;line-height:1;color:${o.colors[Math.floor(shared.rand(0, o.colors.length))]}`, ctx, [{ transform: 'translate(0,0) scale(0) rotate(0deg)', opacity: 1 }, { transform: `translate(${shared.rand(-14, 14)}px,${shared.rand(4, 26)}px) scale(1) rotate(${shared.rand(-90, 90)}deg)`, opacity: 0 }], { duration: shared.rand(500, 800), easing: 'ease-out' }, '✦');
1560
1229
  };
1561
1230
  el.addEventListener('pointermove', move, { passive: true });
1562
1231
  return () => el.removeEventListener('pointermove', move);
@@ -1580,7 +1249,7 @@ const CURSOR_FX = [
1580
1249
  const leave = () => ((p.x = -1e4), (p.y = -1e4));
1581
1250
  el.addEventListener('pointermove', move, { passive: true });
1582
1251
  el.addEventListener('pointerleave', leave);
1583
- const off = canvasBackground(el, ctx, {
1252
+ const off = generative.canvasBackground(el, ctx, {
1584
1253
  draw: ({ ctx: g, w, h }) => {
1585
1254
  g.clearRect(0, 0, w, h);
1586
1255
  if (o.background !== 'transparent') {
@@ -1614,7 +1283,7 @@ const CURSOR_FX = [
1614
1283
  description: 'A soft light follows the pointer across the element, easing behind it (persistent; `color`, `size`).',
1615
1284
  defaults: { color: '#ffffff', size: 260, ease: 0.18, touch: false },
1616
1285
  run: (el, o) => {
1617
- const [r, g, b] = hexRgb(o.color);
1286
+ const [r, g, b] = generative.hexRgb(o.color);
1618
1287
  const s = document.createElement('span');
1619
1288
  s.setAttribute('aria-hidden', 'true');
1620
1289
  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 +1369,7 @@ function bindGesture(el, gesture, effect, o = {}) {
1700
1369
  if (typeof effect === 'function')
1701
1370
  effect(d, e);
1702
1371
  else
1703
- components_fx.playEffect(el, effect, effectOptions, e).catch(() => undefined);
1372
+ registry.playEffect(el, effect, effectOptions, e).catch(() => undefined);
1704
1373
  };
1705
1374
  const twistAngle = () => {
1706
1375
  const [a, b] = [...pts.values()];
@@ -1814,7 +1483,7 @@ function defineGestureFx(tag = 'usa-gesture-fx') {
1814
1483
  }
1815
1484
  const target = this.flag('self') ? this : this.firstElementChild || this;
1816
1485
  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), {
1486
+ this.onCleanup(bindGesture(this, this.gesture, (_d, e) => void registry.playEffect(target, effect, effectOptions, e).catch(() => undefined), {
1818
1487
  velocity: this.num('velocity', 0.8),
1819
1488
  angle: this.num('angle', 30),
1820
1489
  duration: this.num('duration', 650),
@@ -1855,11 +1524,11 @@ function bumpCount(el, delta, ctx) {
1855
1524
  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
1525
  /** A few glyphs flying out of the pointer / centre. */
1857
1526
  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);
1527
+ const { x, y } = shared.origin(el, ctx);
1528
+ return shared.all(Array.from({ length: n }, (_, i) => {
1529
+ const a = rise ? -Math.PI / 2 + shared.rand(-0.6, 0.6) : (i / n) * Math.PI * 2;
1530
+ const d = shared.rand(28, 56);
1531
+ return shared.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: shared.rand(500, 750), easing: 'cubic-bezier(.2,.8,.3,1)' }, glyph);
1863
1532
  }));
1864
1533
  }
1865
1534
  const click = (name, description, defaults, run) => ({ name, kind: 'click', description, defaults, run });
@@ -1902,7 +1571,7 @@ const MICRO_FX = [
1902
1571
  if (el.getAttribute('aria-busy') === 'true')
1903
1572
  return;
1904
1573
  el.setAttribute('aria-busy', 'true');
1905
- const [bar, remove] = overlay(el, `background:${o.color}55;transform-origin:left;transform:scaleX(0)`);
1574
+ const [bar, remove] = shared.overlay(el, `background:${o.color}55;transform-origin:left;transform:scaleX(0)`);
1906
1575
  const a = ctx.animate(bar, [{ transform: 'scaleX(0)' }, { transform: 'scaleX(1)' }], { duration: o.duration, easing: 'linear', fill: 'forwards' });
1907
1576
  const done = a ? a.finished.catch(() => undefined) : Promise.resolve();
1908
1577
  return done.then(() => {
@@ -1928,12 +1597,12 @@ const MICRO_FX = [
1928
1597
  click('send-plane', 'A paper plane ✈ takes off from the button.', { color: '#22d3ee' }, (el, o, ctx) => {
1929
1598
  const r = el.getBoundingClientRect();
1930
1599
  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;
1600
+ return shared.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
1601
  }),
1933
1602
  click('add-to-cart', 'Bumps `[data-count]` by one and floats a "+1" (`text`).', { text: '+1', color: '#34d399' }, (el, o, ctx) => {
1934
1603
  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;
1604
+ const { x, y } = shared.origin(el, ctx);
1605
+ return shared.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
1606
  }),
1938
1607
  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
1608
  click('upvote', 'Toggles an upvote: the arrow nudges up and `[data-count]` ±1.', {}, (el, _o, ctx) => {
@@ -1967,7 +1636,7 @@ const MICRO_FX = [
1967
1636
  }),
1968
1637
  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
1638
  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`);
1639
+ const [b, remove] = shared.overlay(el, `display:grid;place-items:center;font:700 22px system-ui;color:#fff;background:${o.color}d0`);
1971
1640
  b.textContent = '✓';
1972
1641
  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
1642
  return a ? a.finished.then(remove, remove) : void setTimeout(remove, 900);
@@ -2082,7 +1751,7 @@ function themePreset(t, role) {
2082
1751
  function playThemeEffect(el, role, theme) {
2083
1752
  const name = theme || el.closest('[data-usa-theme]')?.getAttribute('data-usa-theme') || 'neon';
2084
1753
  const pr = themePreset(name, role);
2085
- return components_fx.playEffect(el, pr.effect, pr.options);
1754
+ return registry.playEffect(el, pr.effect, pr.options);
2086
1755
  }
2087
1756
  const THEME_FX = [
2088
1757
  {
@@ -2105,7 +1774,7 @@ const THEME_FX = [
2105
1774
  description: 'A bright diagonal shine sweeps across frosted glass.',
2106
1775
  defaults: { duration: 700 },
2107
1776
  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');
1777
+ const [s, remove] = shared.overlay(el, 'overflow:hidden;background:linear-gradient(105deg,transparent 35%,#ffffff8c 50%,transparent 65%);background-size:250% 100%;background-position:120% 0');
2109
1778
  const a = ctx.animate(s, [{ backgroundPosition: '120% 0' }, { backgroundPosition: '-20% 0' }], { duration: o.duration, easing: 'ease-in-out' });
2110
1779
  return a ? a.finished.then(remove, remove) : void remove();
2111
1780
  },
@@ -2117,7 +1786,7 @@ const THEME_FX = [
2117
1786
  reduced: 'run',
2118
1787
  defaults: { opacity: 0.18 },
2119
1788
  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`);
1789
+ const [s, remove] = shared.overlay(el, `opacity:${o.opacity};background:repeating-linear-gradient(0deg,#000 0 1px,transparent 1px 3px);mix-blend-mode:multiply`);
2121
1790
  const a = ctx.reduced ? null : ctx.animate(s, [{ backgroundPosition: '0 0' }, { backgroundPosition: '0 30px' }], { duration: 2400, iterations: Infinity, easing: 'linear' });
2122
1791
  return () => {
2123
1792
  a?.cancel();
@@ -2152,7 +1821,7 @@ function defineTheme(tag = 'usa-theme') {
2152
1821
  const pr = themePreset(this.theme, role);
2153
1822
  const trigger = role === 'attention' ? 'click' : role === 'background' ? 'load' : role;
2154
1823
  try {
2155
- this.onCleanup(components_fx.bindEffect(el, pr.effect, { ...(pr.options || {}), trigger }));
1824
+ this.onCleanup(registry.bindEffect(el, pr.effect, { ...(pr.options || {}), trigger }));
2156
1825
  }
2157
1826
  catch {
2158
1827
  /* effect not registered: call registerAllEffects() */
@@ -2271,7 +1940,7 @@ function createPlayer(root, animation, o = {}) {
2271
1940
  if (!e.fired && time >= e.t.start) {
2272
1941
  e.fired = true;
2273
1942
  for (const el of e.els)
2274
- components_fx.playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
1943
+ registry.playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
2275
1944
  }
2276
1945
  };
2277
1946
  const done = () => {
@@ -2479,7 +2148,7 @@ const EFFECT_PACKS = {
2479
2148
  'cards-click': [...CARD_FX, ...CLICK_FX],
2480
2149
  physics: PHYSICS_FX,
2481
2150
  page: PAGE_FX,
2482
- generative: GENERATIVE_FX,
2151
+ generative: generative.GENERATIVE_FX,
2483
2152
  audio: AUDIO_FX,
2484
2153
  cursor: CURSOR_FX,
2485
2154
  micro: MICRO_FX,
@@ -2487,32 +2156,32 @@ const EFFECT_PACKS = {
2487
2156
  };
2488
2157
  /** 5.1: card & click effects 2.0. */
2489
2158
  function registerCardClickEffects() {
2490
- components_fx.registerEffects(EFFECT_PACKS['cards-click']);
2159
+ registry.registerEffects(EFFECT_PACKS['cards-click']);
2491
2160
  }
2492
2161
  /** 5.2: bounce & physics micro-interactions. */
2493
2162
  function registerPhysicsEffects() {
2494
- components_fx.registerEffects(EFFECT_PACKS.physics);
2163
+ registry.registerEffects(EFFECT_PACKS.physics);
2495
2164
  }
2496
2165
  /** 5.3: page-wide transitions and effects. */
2497
2166
  function registerPageEffects() {
2498
- components_fx.registerEffects(EFFECT_PACKS.page);
2167
+ registry.registerEffects(EFFECT_PACKS.page);
2499
2168
  }
2500
2169
  /** 5.5: generative Canvas 2D backgrounds. */
2501
2170
  function registerGenerativeEffects() {
2502
- components_fx.registerEffects(EFFECT_PACKS.generative);
2171
+ registry.registerEffects(EFFECT_PACKS.generative);
2503
2172
  }
2504
2173
  /** 5.6: sound-reactive (Web Audio) backgrounds. */
2505
2174
  function registerAudioEffects() {
2506
- components_fx.registerEffects(EFFECT_PACKS.audio);
2175
+ registry.registerEffects(EFFECT_PACKS.audio);
2507
2176
  }
2508
2177
  /** 5.7: cursor trails, magnetic dots, spotlight cursor. */
2509
2178
  function registerCursorEffects() {
2510
- components_fx.registerEffects(EFFECT_PACKS.cursor);
2179
+ registry.registerEffects(EFFECT_PACKS.cursor);
2511
2180
  }
2512
2181
  /** 5.8: micro-interactions + theme-pack effects. */
2513
2182
  function registerMicroEffects() {
2514
- components_fx.registerEffects(EFFECT_PACKS.micro);
2515
- components_fx.registerEffects(EFFECT_PACKS.themes);
2183
+ registry.registerEffects(EFFECT_PACKS.micro);
2184
+ registry.registerEffects(EFFECT_PACKS.themes);
2516
2185
  }
2517
2186
  /** Define the 5.x elements of this entry (`<usa-story>`, …) under their default tags. */
2518
2187
  function defineEffectElements() {
@@ -2526,16 +2195,20 @@ function defineEffectElements() {
2526
2195
  function registerAllEffects() {
2527
2196
  components_fx.registerBuiltinEffects();
2528
2197
  for (const defs of Object.values(EFFECT_PACKS))
2529
- components_fx.registerEffects(defs);
2198
+ registry.registerEffects(defs);
2530
2199
  }
2531
2200
 
2201
+ exports.fxLayer = shared.fxLayer;
2202
+ exports.GENERATIVE_FX = generative.GENERATIVE_FX;
2203
+ exports.canvasBackground = generative.canvasBackground;
2204
+ exports.hexRgb = generative.hexRgb;
2205
+ exports.noise2 = generative.noise2;
2532
2206
  exports.ANIMATION_FORMAT = ANIMATION_FORMAT;
2533
2207
  exports.AUDIO_FX = AUDIO_FX;
2534
2208
  exports.CARD_FX = CARD_FX;
2535
2209
  exports.CLICK_FX = CLICK_FX;
2536
2210
  exports.CURSOR_FX = CURSOR_FX;
2537
2211
  exports.EFFECT_PACKS = EFFECT_PACKS;
2538
- exports.GENERATIVE_FX = GENERATIVE_FX;
2539
2212
  exports.GESTURES = GESTURES;
2540
2213
  exports.MICRO_FX = MICRO_FX;
2541
2214
  exports.PAGE_FX = PAGE_FX;
@@ -2551,7 +2224,6 @@ exports.bindBeat = bindBeat;
2551
2224
  exports.bindGesture = bindGesture;
2552
2225
  exports.bounceKeyframes = bounceKeyframes;
2553
2226
  exports.bumpCount = bumpCount;
2554
- exports.canvasBackground = canvasBackground;
2555
2227
  exports.createBeatDetector = createBeatDetector;
2556
2228
  exports.createPlayer = createPlayer;
2557
2229
  exports.defineAudio = defineAudio;
@@ -2564,10 +2236,7 @@ exports.disableAudio = disableAudio;
2564
2236
  exports.enableAudio = enableAudio;
2565
2237
  exports.flingVelocity = flingVelocity;
2566
2238
  exports.formatCount = formatCount;
2567
- exports.fxLayer = fxLayer;
2568
2239
  exports.getAudio = getAudio;
2569
- exports.hexRgb = hexRgb;
2570
- exports.noise2 = noise2;
2571
2240
  exports.normalizeAnimation = normalizeAnimation;
2572
2241
  exports.onBeat = onBeat;
2573
2242
  exports.playThemeEffect = playThemeEffect;