strategy pattern · shared core · ES modules
sparkle.js — continuous ambient · class-driven · data-sparkle config
burst.js — single volley · fire-and-forget · Burst.fire(el)
Burst.fireAt(e.clientX, e.clientY) — detached from any element
// ── Module structure ───────────────────────────────────────────────────── // // sparkle-core.js shared: keyframes, spawnParticle, ParticleEmitter // ├── sparkle.js ContinuousStrategy + SparkleController // └── burst.js BurstStrategy + BurstController // ── sparkle.js ─────────────────────────────────────────────────────────── import { Sparkle } from './sparkle.js'; Sparkle.observe(); // one call — watches document.body // Declarative config via data attribute: // <div data-sparkle='{"colors":["#FF6B9D"],"count":6}' class="is-sparkling"> // ── burst.js ───────────────────────────────────────────────────────────── import { Burst } from './burst.js'; Burst.fire(el); // from element centre Burst.fire(el, { count: 40, speed: 180 }); Burst.fireAt(e.clientX, e.clientY); // anywhere on the page // ── Strategy pattern ───────────────────────────────────────────────────── // // ParticleEmitter.start(strategy) // → strategy.onStart(emitter) ← ContinuousStrategy: setInterval // ← BurstStrategy: fire all, self-stop // → strategy.onStop(emitter) ← both: clearInterval / noop // // animationend cleans up DOM — no rAF anywhere