Skip to main content

Documentation Index

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

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

EmberCursor transforms the mundane system cursor into a signature piece of Web Weaver’s aesthetic: a glowing teal ember that leaves a trail of luminous particles as it moves. The native cursor is hidden across the entire site, and every mouse movement spawns a new flicker of light. The result is an immersive, witchcraft-inspired interaction layer that makes even idle browsing feel atmospheric. This component runs continuously and silently in the background — visitors will notice its magic without ever needing to trigger it deliberately.

Visual behaviour

When the page loads, the system cursor is hidden via a global cursor: none rule applied to body. In its place, EmberCursor renders two visual layers:
  • Glowing dot — A small, intensely lit teal circle that tracks the exact pointer position in real time. It has a soft box-shadow bloom to simulate ember light.
  • Trailing particles — On each mousemove event, one or more translucent ember particles are spawned at the cursor position. Each particle animates outward, fades, and is removed, creating the illusion of a smouldering trail.
Both layers are Framer Motion motion.div elements, giving each particle its own independent spring or tween animation.

Where it’s used

EmberCursor is mounted once inside the Layout component, outside the React Router <Routes> tree. This means it is present on every page of the portfolio without any page-level import.
// Inside Layout — not needed anywhere else
import EmberCursor from "./EmberCursor";

function Layout() {
  return (
    <>
      <EmberCursor />
      {/* ... rest of app shell */}
    </>
  );
}

Props

EmberCursor is a fully self-contained component and accepts no props. All behaviour — colours, particle count, animation timing, and cursor-hiding CSS — is encapsulated within the module.
Because there are no props, you cannot change the cursor colour or particle behaviour at runtime without editing the source module directly.

Implementation notes

Event listener lifecycle

The component attaches a mousemove listener to window inside a useEffect hook. The listener is cleaned up when the component unmounts, preventing memory leaks. Since Layout never unmounts, the listener lives for the full session.

Framer Motion particles

Each trailing particle is a motion.div with an animate prop driving opacity from 1 → 0 and a small random translation. Once the exit animation completes the element is removed from the DOM, keeping the node count stable.

z-index strategy

The cursor elements use a very high z-index (typically 9999) so they render above all page content, modals, and overlays. pointer-events: none is set on all cursor elements so they never intercept clicks intended for page content.

cursor: none scope

The cursor: none declaration is applied to body (or via a global style) when the component mounts, and restored on unmount. This ensures no residual hidden-cursor state if the component were ever conditionally removed.

Customisation tips

Although EmberCursor is not prop-driven, its source module is the right place for the following tweaks:
Colour — The teal glow is defined by a hex or HSL value in the box-shadow and background styles of the dot element. Swap it for any colour to match a different theme variant.
Particle count — The number of particles spawned per mousemove event is controlled by a small constant inside the handler. Increasing it creates a denser trail; decreasing it gives a more subtle shimmer.
Animation duration — Each particle’s fade-out duration is set in the Framer Motion transition object. Shorter durations (e.g. 0.3s) feel snappy and electric; longer durations (e.g. 1.2s) produce a slow, smouldering drift.

Full usage example

Because the component is a singleton with no configuration surface, its complete usage is simply:
import EmberCursor from "./components/EmberCursor";

// Render once at the root of your app
<EmberCursor />
There is no further setup required — the component self-initialises its event listeners and global CSS on mount.

Build docs developers (and LLMs) love