Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/colorful/llms.txt

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

CursorTrail paints a colourful particle trail that follows the mouse cursor as visitors move around the page. Each particle is a small filled circle drawn onto a full-viewport <canvas> element. As the cursor moves, new particles spawn at the pointer’s coordinates; older particles shrink and fade over 40 animation frames before being removed. Connecting lines are drawn between adjacent particles to form a smooth ribbon effect, cycling through the theme’s six accent colors as the cursor travels.
The cursor trail is purely decorative. It is rendered on a <canvas> element that sits above page content but carries pointer-events-none, meaning it never intercepts clicks, form input, drag events, or any other user interaction.

Implementation details

The component renders a single <canvas> element with the following properties:
<canvas
  ref={canvasRef}
  className="fixed inset-0 pointer-events-none z-30"
  style={{ mixBlendMode: 'multiply' }}
/>
  • fixed inset-0 — the canvas is always sized to the full viewport and does not scroll with the page.
  • pointer-events-none — all pointer and mouse events pass straight through to the elements underneath.
  • z-30 — the canvas layers above standard page content (z-10, z-20) but below the navigation menu (z-50) and its backdrop overlay (z-40), so the trail never obscures interactive controls.
  • mix-blend-mode: multiply — the trail colors blend with the background rather than overpainting it, keeping the effect subtle on the light off-white page background.
Canvas sizing is handled by a resize event listener that sets canvas.width = window.innerWidth and canvas.height = window.innerHeight immediately on mount and on every subsequent window resize. Particle lifecycle — each mousemove event pushes a new particle object { x, y, age: 0, color } onto a ref-backed array. Each animation frame increments the age of every particle. When age exceeds 40, the particle is spliced out of the array. The radius of each circle is 15 * (1 - age / 40) and its opacity is (1 - age / 40) * 0.5, so particles smoothly shrink and fade to nothing rather than disappearing abruptly. Color cycling — the component cycles through six theme accent colors in order, advancing the index on every mousemove event:
const trailColors = [
  '#2dd4bf', // teal-light
  '#ec4899', // rainbow-pink
  '#f59e0b', // rainbow-amber
  '#84cc16', // rainbow-lime
  '#6366f1', // rainbow-indigo
  '#a855f7', // rainbow-purple
];
Connecting lines — after drawing each circle, the component draws a stroke from the previous particle to the current one using the same color and a line width of particleRadius * 2, with lineCap: 'round' and lineJoin: 'round' for smooth joins. The requestAnimationFrame loop calls clearRect on the entire canvas each frame before redrawing all active particles, keeping the animation in sync with the browser’s display refresh rate. The loop is cancelled via cancelAnimationFrame when the component unmounts.

How it is loaded

CursorTrail is imported and rendered inside assets/main.js, the Vite entry point shared by every HTML page in the theme. Each HTML file pre-declares all three component scripts using <link rel="modulepreload"> in its <head>:
<link rel="modulepreload" crossorigin href="./components/CursorTrail.js">
<link rel="modulepreload" crossorigin href="./components/Navigation.js">
<link rel="modulepreload" crossorigin href="./components/FluidBackground.js">
modulepreload tells the browser to fetch and parse each module as early as possible — before main.js even executes — eliminating the network round-trip latency that would otherwise occur the first time each module is imported.

Disabling the effect

Because CursorTrail is mounted from assets/main.js, removing it requires a single change in that file. Locate the import and the JSX render call, then comment out or delete both lines:
// Remove or comment out the import:
// import { C as CursorTrail } from '../components/CursorTrail.js';

// Remove or comment out the render:
// <CursorTrail />
The modulepreload links in each HTML file can also be removed to avoid the browser fetching the unused module, though leaving them in place causes no functional harm.

Accessibility

The canvas element has no accessible role or label because it carries no semantic content — it is a purely visual embellishment. Assistive technologies ignore it by default.
The mousemove event used to generate particles only fires on pointer devices. On touch-only devices — phones, tablets, and some hybrid laptops in touch mode — no trail is drawn and the canvas remains blank. You can verify this behavior by opening the theme on a touch device or by toggling touch emulation in browser developer tools. No additional code is needed to support touch devices; the trail simply does not activate.
For users who prefer reduced motion, consider pairing the trail with a prefers-reduced-motion check so that particles are not spawned when the system preference is set:
const prefersReducedMotion =
  window.matchMedia('(prefers-reduced-motion: reduce)').matches;

window.addEventListener('mousemove', (e) => {
  if (prefersReducedMotion) return;
  // ... spawn particle
});
See the accessibility guide for broader motion-reduction recommendations across the theme.

Build docs developers (and LLMs) love