Skip to main content

Documentation Index

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

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

CandleCursor hides the native cursor site-wide and renders a layered flame effect in its place. The flame is built from three concentric layers — a white-yellow inner flame, a pumpkin-orange outer flame, and a wide diffuse glow — and follows the mouse pointer with spring physics so it trails the cursor with a natural, organic lag. When the user hovers over a link or button, the entire flame scales up to 1.5× its normal size, providing tactile hover feedback without any additional CSS.

How it works

The component manages three pieces of React state: the current (x, y) mouse position, a boolean isHovering flag for interactive elements, and an isVisible flag that starts false until the mouse has moved at least once (preventing the flame from flashing at the origin 0, 0 on page load). Three window event listeners are registered in a useEffect:
  • mousemove — updates the (x, y) position state and sets isVisible to true on the first movement
  • mouseover — inspects the event target: if it is an <a>, <button>, or any element with [role="button"] (checked via tagName and closest()), isHovering is set to true; otherwise it is reset to false
  • document.mouseleave — sets isVisible to false when the pointer leaves the browser window entirely
The outer container is a framer-motion motion.div fixed at z-[9999] with pointer-events-none and mix-blend-screen. Its animate prop drives the x and y position, offset by −10px on each axis to centre the flame on the hotspot. The transition uses spring physics with type: 'spring', stiffness: 500, damping: 28, and mass: 0.5 for smooth, responsive lag. Inside the container a second motion.div animates its scale between 1 (idle) and 1.5 (hovering) to produce the hover bloom. The three flame layers are absolutely positioned div elements:
LayerClassesEffect
White-yellow inner flamew-2 h-3, bg-yellow-200, blur-[1px], animate-flickerSharp bright core
Orange mid flamew-4 h-6, bg-[#ff7518], blur-[3px], opacity-80, animate-flickerMain flame body
Orange diffuse gloww-8 h-12, bg-[#ff7518], blur-[10px], opacity-30, animate-flickerSoft ambient halo
Each layer carries the animate-flicker class (with staggered animationDelay values of 0s, 0.1s, and 0.2s) so the layers pulse slightly out of phase, creating a more organic flicker. When isHovering is true, an AnimatePresence smoke puff is mounted: a small gray motion.div that starts at opacity: 0.5, scale: 0.5, y: 0 and animates to opacity: 0, scale: 1.5, y: -20 over 500 ms, giving the impression of a flame-blown smoke wisp. The component returns null when isVisible is false, so it is completely absent from the DOM until the mouse moves.

CSS integration

The native cursor is suppressed globally for all interactive element types in main.css:
body, a, button, [role=button], input, select, textarea {
  cursor: none;
}
This rule ensures the system cursor never appears alongside the custom flame, even over form fields and other focusable elements.

Cursor visibility on touch devices

Touch devices do not fire mousemove events, so the isVisible state remains false and the component returns null for the entire session — it is effectively invisible on mobile without any additional logic. If you need to explicitly restore the native cursor on touch devices (for example after progressive enhancement), you can layer a media query on top of the cursor: none rule:
@media (hover: none) {
  body, a, button, [role=button], input, select, textarea {
    cursor: auto;
  }
}

Customizing the flame

All flame appearance values are straightforward to tweak in the source before rebuilding:
  • Color — both the mid-flame and diffuse-glow layers use bg-[#ff7518] (pumpkin orange). Replace #ff7518 with any CSS color to change the flame hue.
  • Size — the three layers use Tailwind width/height utility classes (w-2 h-3, w-4 h-6, w-8 h-12). Increase or decrease these to make the flame larger or smaller.
  • Spring physics — the transition object { type: 'spring', stiffness: 500, damping: 28, mass: 0.5 } controls how tightly the flame tracks the cursor. Increase stiffness for a snappier feel, decrease it for more float. Increase damping to reduce oscillation.
The mix-blend-screen blend mode applied to the cursor container makes the flame appear luminous and additive against the dark #1a1a1d background — light colors mix brightly while dark areas become transparent, so the flame looks like real light rather than a painted graphic. If you switch the theme to a light background, you may want to change the blend mode to mix-blend-multiply for a comparable effect.

Build docs developers (and LLMs) love