Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/web-surfer/llms.txt
Use this file to discover all available pages before exploring further.
SparkleCursor adds a whimsical star-particle trail to the mouse cursor across the entire Web Surfer site. As you move the cursor, tiny 5-pointed stars spawn around it, drift downward, spin, and fade out — all drawn in the Y2K colour palette of teal, hot pink, and lime green. The effect is purely cosmetic and never interferes with clicks or keyboard navigation.
How it works
The component renders a single<canvas id="sparkle-canvas"> element that is fixed to the viewport and covers it completely. All drawing happens through the Canvas 2D API; no DOM elements are created per particle.
Canvas setup
On mount, the canvas dimensions are set to
window.innerWidth × window.innerHeight. A resize listener keeps the canvas in sync whenever the browser window is resized.Mouse tracking
A
mousemove listener on window records the current cursor position. On each move event, there is a 50 % chance a new star particle is spawned near the cursor. A 100 ms debounce timeout marks the cursor as inactive if it stops moving.Particle creation
Spawned particles receive randomised properties — position, size, colour, lifespan, and rotation — and are pushed into an in-memory array managed via a
useRef.Animation loop
A
requestAnimationFrame loop clears the canvas each frame, updates every particle (move down 1 px, rotate, age by 1), draws it as a 5-pointed star at the correct opacity, and removes it once its lifespan is exhausted.#00CED1— Dark Turquoise (teal)#FF69B4— Hot Pink#9AE600— Lime Green
save / restore so each star’s rotation is isolated.
Particle properties
| Property | Range | Description |
|---|---|---|
size | 4–12 px | Random star outer radius (Math.random() * 8 + 4) |
color | #00CED1, #FF69B4, #9AE600 | Randomly chosen from the palette at spawn |
maxLife | 20–50 frames | How many animation frames before the star is removed (Math.random() * 30 + 20) |
rotationSpeed | ±0.2 rad/frame | Spin direction and speed ((Math.random() - 0.5) * 0.2) |
| position jitter | ±20 px | Random offset from the exact cursor position ((Math.random() - 0.5) * 20) |
1 - life / maxLife, so stars start fully opaque and linearly fade to transparent over their lifetime. Size also scales with this opacity factor, making stars appear to shrink as they fade.
Accessibility
SparkleCursor checks window.matchMedia("(prefers-reduced-motion: reduce)").matches at the start of its useEffect. If the user has requested reduced motion in their operating system or browser settings, the effect exits immediately — no event listeners are attached, no requestAnimationFrame loop is started, and the canvas remains blank.
Performance
- The
requestAnimationFrameloop is the only animation mechanism — there are nosetIntervaltimers or CSS animations involved. - The particle array is stored in a
useRefso updates never trigger React re-renders. - The canvas is cleared completely each frame with
clearRect, which is faster than compositing a growing list of DOM elements. - The
resizehandler simply updatescanvas.widthandcanvas.height; the browser discards the current pixel buffer on assignment, so no manual clearing is needed. - On unmount,
cancelAnimationFrame(animationFrameId)stops the loop immediately, preventing memory leaks in single-page-app navigation.
Usage
SparkleCursor accepts no props. It is already embedded directly inside the Layout component, so it is active on every page of the site automatically:
<SparkleCursor /> line from Layout.js.
The sparkle canvas is positioned at
z-index: 9999 so it floats above all other content, but it has pointer-events: none applied via the #sparkle-canvas CSS rule in main.css. This means the canvas never intercepts mouse clicks, hover states, or touch events — all interactions pass straight through to the elements beneath it.