Skip to main content

Documentation Index

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

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

FamiliarCursor replaces the browser’s default cursor with a two-part witch-themed cursor that follows the mouse using Framer Motion spring physics. A small teal dot tracks the pointer precisely, while a larger plum witch shape trails slightly behind — creating the sensation of a creature following your hand rather than a mechanical pointer.

Touch device handling

The component performs a coarse-pointer media query check before rendering anything. On touch devices (mobile and tablet), no cursor elements are rendered, preserving the native touch experience:
if (typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches) {
  return null;
}
This check runs synchronously on component mount. On coarse-pointer devices the component returns null immediately, so no event listeners or motion values are created.

The two cursor parts

The cursor is composed of two independent Framer Motion elements that track the mouse position in different ways:

Small dot

An 8×8px (w-2 h-2) bg-witch-turquoise circle that uses animate with a short tween transition (duration: 0.1, ease: backOut). It responds nearly instantly, always sitting exactly on the pointer hotspot.

Witch SVG

A 32×32px plum SVG shape that uses style={{ x: springX, y: springY }} — Framer Motion spring values. It lags behind the real pointer, drifting in to catch up with organic deceleration.
Both elements are fixed top-0 left-0 with pointer-events-none so they never interfere with clicks or hovers on the underlying page.

Spring configuration

The witch SVG’s following motion is controlled by a useSpring call with a hand-tuned config:
const springConfig = { damping: 25, stiffness: 150, mass: 0.5 };
const springX = useSpring(0, springConfig);
const springY = useSpring(0, springConfig);
ParameterValueEffect
damping25Controls how quickly the oscillation settles. Higher values stop the overshoot sooner.
stiffness150Controls the pull strength toward the target. Higher values make the cursor follow more eagerly.
mass0.5A lighter mass reaches the target faster, making the lag feel brief rather than sluggish.
On every mousemove event, springX and springY are updated to clientX - 16 and clientY - 16 (offsetting by half the SVG’s 32px width to center it on the pointer).

Hover detection

A mouseover listener checks whether the event target — or any of its ancestors — is an interactive element:
const isInteractive =
  target.tagName.toLowerCase() === "a" ||
  target.tagName.toLowerCase() === "button" ||
  target.closest("a") ||
  target.closest("button") ||
  target.closest('[role="button"]');
When isHovered is true, both cursor parts play their hover animations:
PartHover animation
Small dotscale: 2, opacity: 0.8
Witch SVGrotate: 15deg, scale: 1.2
The rotation on the witch SVG gives the impression of the familiar leaning in with curiosity when the cursor approaches a link or button.

The witch SVG shape

The SVG is a flame or teardrop silhouette with two wing-like side paths and a small amber circle for the eye:
<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
  {/* Main body — flame/teardrop shape */}
  <path d="M12 2C10 4 8 6 7 9C6 12 5 15 7 18C8 19 10 20 12 20C14 20 16 19 17 18C19 15 18 12 17 9C16 6 14 4 12 2Z" />
  {/* Left wing */}
  <path d="M7 9C4 10 2 12 2 14C2 15 3 16 5 16C7 16 8 15 9 13" />
  {/* Right wing */}
  <path d="M17 9C20 10 22 12 22 14C22 15 21 16 19 16C17 16 16 15 15 13" />
  {/* Amber eye */}
  <circle cx="10" cy="10" r="1" fill="#f59e0b" />
</svg>
The component is styled text-witch-plum opacity-80 drop-shadow-[0_0_8px_rgba(88,28,135,0.8)], so the SVG inherits the plum color and glows slightly via the drop shadow.

Customization

Spring feel — increase stiffness (e.g. 250) for a snappier follow; decrease damping (e.g. 15) for more bounce on arrival. Hover selectors — add extra selectors to the hover detection block to trigger the enlarged state on other interactive elements, such as [data-hoverable] or input. SVG shape — replace the <path> elements inside the witch SVG with any other shape. Keep the viewBox="0 0 24 24" and fill="currentColor" so the color inheritance continues to work. Dot color — change bg-witch-turquoise on the small dot to any other design token for a different accent color.
Both cursor elements are positioned with fixed top-0 left-0. Their visual position depends entirely on the x/y transform values. Do not add top/left offsets to either element — they will break the positioning logic.

Build docs developers (and LLMs) love