Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/y2k-webring/llms.txt

Use this file to discover all available pages before exploring further.

SparkleTrail is a presentational component with no props. It adds a ✦ sparkle trail behind the mouse cursor — neon glyphs that bloom out and fade as the pointer moves across the screen. The effect is intentionally retro, recalling the cursor trails of early-2000s personal homepages, but implemented with Framer Motion for smooth, composited animation. It is enabled exclusively on the Home page so the effect feels special rather than overwhelming.

How It Works

SparkleTrail manages its sparkle queue entirely through React state and useEffect, with no external dependencies beyond Framer Motion.
1

Mouse event listener

On mount, SparkleTrail attaches a mousemove listener to window. On unmount it is removed via the useEffect cleanup function, so there is no memory leak when navigating away from the Home page.
2

16ms throttle

Each mousemove callback records a timestamp via useRef. If fewer than 16ms have elapsed since the last processed event the callback returns early. This caps sparkle spawning to roughly 60 events per second, matching a typical display refresh rate.
3

20px velocity threshold

Even within the 16ms window, a new sparkle is only spawned when the cursor has moved at least 20px from the position where the last sparkle was placed (calculated as Euclidean distance). This prevents a cluster of overlapping sparkles when the mouse slows down or pauses.
4

Sparkle queue — max 8 active

Approved sparkles are appended to a state array. If the array would exceed 8 entries the oldest sparkle is sliced off the front, keeping the active sparkle count bounded and preventing DOM growth during fast mouse movements.
5

Auto-removal after 400ms

A useEffect that watches the sparkle array schedules a setTimeout for 400ms whenever the array is non-empty. When it fires, the first (oldest) sparkle is removed from the array. This matches the animation duration so sparkles disappear from state just as they finish their exit animation.
6

Per-sparkle animation

Each sparkle is a motion.div rendering the ✦ glyph character. It animates from opacity: 1, scale: 0.5, rotate: 0 to opacity: 0, scale: 1.5, rotate: 90 over 400ms with an easeOut easing curve, giving a bloom-and-fade effect. The sparkle is positioned with position: absolute at the exact clientX / clientY of the mouse event.

Sparkle colors

Each spawned sparkle is assigned a random color from the following four-value cycle:
Color nameHex value
Magenta#ff00aa
Purple#c026d3
Lime#aeff00
Cyan#00e5ff
The color is applied via inline style={{ color, textShadow: '0 0 5px {color}' }}, producing a matching neon glow around each glyph.

Usage

SparkleTrail accepts no props and is rendered conditionally in AppLayout based on the current route. It should only ever appear on the Home page (/):
import { SparkleTrail } from '../components/y2k/SparkleTrail';
import { useLocation } from 'react-router-dom';

export function AppLayout({ children }) {
  const isHome = useLocation().pathname === '/';

  return (
    <div className="min-h-screen p-4 ...">
      {isHome && <SparkleTrail />}
      {/* rest of layout */}
    </div>
  );
}
Because SparkleTrail mounts and unmounts with the Home route, the window event listener is automatically cleaned up on every navigation away from /.

Performance Notes

Two safeguards keep the effect lightweight:
  • 16ms throttle — processes at most one event per frame, avoiding redundant state updates during fast mouse sweeps.
  • 20px minimum movement — suppresses sparkle creation when the cursor is nearly stationary, which is where the effect would produce an unsightly pile-up rather than a trail.
Together these mean that even during aggressive mouse movement the active DOM node count stays at or below 8 motion.div elements at any given moment.
SparkleTrail renders into a container with position: fixed; inset: 0; pointer-events: none; z-index: 50. The fixed positioning keeps it covering the full viewport regardless of scroll position, pointer-events: none ensures the overlay never intercepts clicks or hover events on page content, and z-index: 50 places it above all standard page elements while remaining below any modal or dialog layers that should use z-index: 100 or higher.

Build docs developers (and LLMs) love