Instead of swapping cursor images, a single SVG arrow resolves its state from whatever is under the pointer. Clickable things fill it solid, cards and media turn it into a labelled pill, and a pending route makes it pulse. Gaps between neighbouring buttons stay clickable. It's the backbone of how this site communicates interactivity.
One fixed SVG cursor resolves its state from the element under the pointer: interactive elements (links, buttons, inputs, summary, anything marked data-clickable) fill the arrow solid and soften its corners, and any element with data-cursor-label swaps the arrow for a floating pill that names the action. Buttons inside a data-cursor-group="buttons" keep the clickable state across the gaps between them. The outline and fill share one path whose numbers are interpolated between a sharp arrow and a rounded one with GSAP, so every state change is a morph rather than an image swap. The sliders feed morph time, roundness, fill fade and loading pulse through a window event; the defaults shown are the live site's values. The system worked well enough that I took it outside the site: the same shapes are rendered as real Windows cursors, including the loading pulse as an animated one, and I run them as the system cursor on my own PCs. The pack and installer are in the repo.
const interpolateCursorPath = (fromPath, toPath, amount) => {
const fromNumbers = fromPath.match(/-?\d*\.?\d+/g)?.map(Number) || [];
let index = 0;
return toPath.replace(/-?\d*\.?\d+/g, (match) => {
const from = fromNumbers[index] ?? Number(match);
const to = Number(match);
index += 1;
return String(from + (to - from) * Math.max(0, Math.min(1, amount)));
});
};
const interactiveTarget = target.closest(INTERACTIVE_SELECTOR);
const interactiveLabel = interactiveTarget?.getAttribute("data-cursor-label");
const labeledTarget = target.closest("[data-cursor-label]");
if (interactiveLabel) {
return { text: interactiveLabel, variant: "project", clickable: false };
}
if (interactiveTarget) {
return { text: "", variant: "default", clickable: true };
}
if (labeledTarget) {
return {
text: labeledTarget.getAttribute("data-cursor-label") || "",
variant: labeledTarget.getAttribute("data-cursor-variant") || "project",
clickable: false,
};
}
gsap.to([outlinePath, fillPath], {
attr: { d: targetPath },
duration: labConfig.morph,
ease: "power1.out",
overwrite: "auto",
});