Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dyed-in-the-wool/llms.txt

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

Overview

DyeDropCursor replaces the native browser cursor with a motion.div teal circle that follows the mouse using a Framer Motion spring animation. As you move, it continuously spawns small colored circles — “ink drops” — that scale up and fade out over one second, leaving a trail reminiscent of ink dispersing through wet fabric.
DyeDropCursor does not hide the native browser cursor. The component’s container carries only pointer-events-none fixed inset-0 z-[100] overflow-hidden — no cursor: none style is applied. If you want to suppress the system cursor, add cursor-none to the <html> or <body> element in your global CSS.
DyeDropCursor is a purely cosmetic, mouse-driven effect. It listens exclusively to mousemove and mouseover events and has no effect on touch or mobile devices. On touchscreens the component renders but produces no visible output, and the absence of a custom cursor has no functional impact.

Ink Drop Color Palette

Each ink drop is assigned a random color from the following array on spawn:
const colors = [
  "#0d9488", // teal-600
  "#2dd4bf", // teal-400
  "#99f6e4", // teal-200
  "#db2777", // pink-600 (magenta)
  "#0369a1", // sky-700
];
The five-color spread — from deep teal through aqua to magenta and ocean blue — maps directly to the dye-* palette used throughout the site.

Interactive Element Behavior

When the cursor moves over an <a> or <button> element (or any element nested inside one, detected via .closest()), the cursor circle:
  • Expands from 24px to 48px diameter
  • Changes color from teal #0d9488 to magenta #db2777
  • Reveals a small inner dot (w-2 h-2, bg-dye-fabric) that scales in with a spring animation
This gives interactive elements a clear affordance without any changes to the elements themselves.

Technical Implementation

Cursor tracking

Mouse position is tracked with window.addEventListener('mousemove'). The cursor motion.div animates to the new position on every frame using a Framer Motion spring:
transition: {
  type: "spring",
  stiffness: 500,
  damping: 28,
  mass: 0.5,
}
The spring parameters are tuned for a snappy, lightweight feel — the cursor follows the pointer closely without feeling mechanical.

Trail drops

On each mousemove event, a random check (Math.random() > 0.6) throttles drop spawning to roughly 40% of frames, preventing the trail from becoming too dense. Each drop is:
  • Positioned at the exact cursor coordinates at the moment of spawn
  • Sized randomly between 10px and 30px
  • Colored randomly from the palette above
The trail array is capped at the last 15 drops. A setInterval running every 100ms shifts the oldest drop off the front of the array, ensuring smooth cleanup. Each drop animates via AnimatePresence:
initial: { opacity: 0.8, scale: 0 }
animate: { opacity: 0,   scale: 2 }
exit:    { opacity: 0 }
transition: { duration: 1, ease: "easeOut" }
The drops also carry mix-blend-multiply blur-[2px] classes — the multiply blend mode means overlapping drops darken each other and interact with the background, while the blur softens their edges for a wet-ink look.

Pointer capture

The entire component is pointer-events-none fixed inset-0 z-[100] overflow-hidden. It never sits in the event-propagation path, so it cannot block clicks, form inputs, or any other interaction.

Usage

DyeDropCursor is already rendered globally inside Layout — you don’t need to add it to individual pages.
// Already rendered globally inside Layout — no manual use needed.
// To use standalone:
import { DyeDropCursor } from './components/DyeDropCursor';

function App() {
  return (
    <>
      <DyeDropCursor />
      {/* rest of your app */}
    </>
  );
}

Customization

Change the ink drop colors

Edit the P array (the color palette constant) at the top of DyeDropCursor.js:
// DyeDropCursor.js
const P = [
  "#your-color-1",
  "#your-color-2",
  "#your-color-3",
  "#your-color-4",
  "#your-color-5",
];
The hover state color (#db2777) is hardcoded in the animate prop of the cursor motion.div — update that separately if you want the interactive-element highlight to match a new palette.

Adjust spring physics

The spring feel is controlled by three values in the cursor’s transition object:
transition: {
  type: "spring",
  stiffness: 500, // higher = snappier response
  damping: 28,    // higher = less oscillation
  mass: 0.5,      // higher = heavier, more lag
}
For a floatier, trailing feel try stiffness: 200, damping: 20, mass: 1. For an instant, pixel-perfect follow try stiffness: 800, damping: 40, mass: 0.3.

Build docs developers (and LLMs) love