Confetti
DOM-based particle burst — no canvas, no third-party lib, no extra runtime dep. The component renders an invisible anchor; call ref.current.fire() to spawn a fresh particle layer at that anchor. Each particle follows projectile motion (initial velocity + gravity), with randomised colour, size, and rotation. GSAP-backed.
Each click mounts a fresh particle layer at the button. Particles clean themselves up after the lifetime expires.
Usage
import { Confetti, ConfettiHandle, Button } from '@bwo-ui/react';
import { useRef } from 'react';
const ref = useRef<ConfettiHandle>(null);
<Confetti ref={ref} count={80} spread={360}>
<Button onClick={() => ref.current?.fire()}>🎉</Button>
</Confetti>
// Override per-fire — for "wins go faster, losses go slower" etc.
ref.current?.fire({ velocity: 800, colors: ['#16a34a'] });Props
| Prop | Type | Default | Description |
|---|---|---|---|
count | number | 60 | Number of particles per fire. |
spread | number | 90 | Spread arc in degrees. 360 = burst in all directions. |
angle | number | 0 | Origin angle in degrees (0 = up). |
velocity | number | 560 | Initial velocity in pixels per second. |
gravity | number | 1400 | Downward pull on particles (px / s²). |
duration | number | 1.6 | Particle lifetime in seconds. |
size | [min, max] | [6, 12] | Particle size range in pixels. |
colors | string[] | — | Palette to randomise from. |
origin | { x: number; y: number } | — | Origin offset within the anchor (default = centre). |
zIndex | number | 9999 | Z-index for the particle layer. |