Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/sys-witch/llms.txt
Use this file to discover all available pages before exploring further.
ParticleField is a self-contained React component that paints a field of slowly drifting neon particles onto an HTML5 <canvas> element. It fills the entire viewport behind all other page content, creating the signature atmospheric glow of Sys.Witch V2 without interfering with mouse events or keyboard navigation. The animation is driven entirely by requestAnimationFrame and cleans up after itself on unmount.
Canvas Setup
The component attaches aref to its <canvas> element and runs a single useEffect to bootstrap the animation loop. On mount it:
- Reads
canvas.getContext("2d")to obtain the 2D drawing context. - Checks
window.matchMedia("(prefers-reduced-motion: reduce)")— if the user has requested reduced motion, the effect returns immediately without starting the loop. - Sets
canvas.width = window.innerWidthandcanvas.height = window.innerHeight, then creates the initial particle array. - Adds a
resizeevent listener that re-runs the same sizing step whenever the viewport dimensions change. - Starts the animation loop via
requestAnimationFrame.
resize listener and calls cancelAnimationFrame with the stored frame ID, preventing memory leaks and ghost animations.
Particle Class
Each particle is an instance of an innerParticle class defined inside the useEffect closure. Its constructor randomises all initial values against the current canvas dimensions:
y < 0), it wraps back to the bottom at a new random horizontal position, creating an infinite upward-drift illusion.
Neon Colors
Particles are randomly assigned one of three neon colors from an inline array. Each color uses an"OPACITY" placeholder string that is swapped for the particle’s individual opacity at draw time:
main.css:
| Variable | Hex | RGBA |
|---|---|---|
--neon-purple | #b026ff | rgba(176, 38, 255, …) |
--neon-cyan | #00f3ff | rgba(0, 243, 255, …) |
--neon-magenta | #ff00ff | rgba(255, 0, 255, …) |
Particle Count
The number of particles is capped to avoid performance degradation on small screens:Animation Loop
Thedraw function called every frame first clears the full canvas rectangle, then iterates every particle — calling update() followed by draw() — before scheduling the next frame:
CSS Classes
The canvas element uses four Tailwind utility classes:| Class | Purpose |
|---|---|
fixed inset-0 | Pins the canvas to all four viewport edges so it never scrolls |
z-0 | Sits behind <main> (z-10) and <PortalNav> (z-40/z-50) |
pointer-events-none | All clicks, drags, and touch events pass straight through to page content |
opacity-60 | Softens the particle layer to 60 % global opacity for a subtler background effect |
Accessibility
The canvas carriesaria-hidden="true", which hides it from the accessibility tree entirely. Screen readers will not announce it, and it carries no semantic meaning. It is purely decorative.
Motion is automatically suppressed for users with
prefers-reduced-motion: reduce set in their OS or browser. The useEffect checks this media query before starting the animation loop, so no particles are rendered for users who prefer reduced motion.Usage in Layout
ParticleField is rendered as the very first child of Layout, before PortalNav and <main>, ensuring it occupies the lowest visual layer:
Disabling ParticleField
To remove the particle background entirely, delete the<ParticleField /> line from Layout.js. No other changes are necessary — the component has no side effects beyond the canvas and its animation loop.