Cursor Follower
Region where a tilted, outlined sticker label trails the mouse pointer with a springy lag and pops in/out on enter/leave. Mouse only; add cursor-none on the region to replace the cursor.
brutalist/effect/cursor-followerSource
"use client";
import { useState, type ReactNode } from "react";
import { motion, useMotionValue, useReducedMotion, useSpring } from "motion/react";
import { cn } from "@/lib/utils";
export interface CursorFollowerProps {
children?: ReactNode;
/** Sticker text that follows the pointer. */
label?: string;
tone?: "primary" | "accent" | "secondary";
className?: string;
}
const TONE = {
primary: "bg-da-primary text-da-primary-fg",
accent: "bg-da-accent text-da-accent-fg",
secondary: "bg-da-secondary text-da-secondary-fg",
} as const;
/**
* Area where a tilted sticker trails the mouse pointer with a springy lag.
* Mouse only (hidden for touch and pen), decorative.
*/
export function CursorFollower({ children, label = "Ship it →", tone = "primary", className }: CursorFollowerProps) {
const reduce = useReducedMotion();
const [visible, setVisible] = useState(false);
const x = useMotionValue(0);
const y = useMotionValue(0);
const sx = useSpring(x, { stiffness: 350, damping: 28 });
const sy = useSpring(y, { stiffness: 350, damping: 28 });
return (
<div
className={cn("relative overflow-hidden", className)}
onPointerMove={(e) => {
if (e.pointerType !== "mouse") return;
const r = e.currentTarget.getBoundingClientRect();
x.set(e.clientX - r.left + 14);
y.set(e.clientY - r.top + 14);
if (!visible) setVisible(true);
}}
onPointerLeave={() => setVisible(false)}
>
{children}
<motion.span
aria-hidden
className={cn(
"da-stroke pointer-events-none absolute top-0 left-0 z-20 -rotate-6 px-3 py-1 font-da-mono text-xs font-bold whitespace-nowrap uppercase shadow-da-sm",
TONE[tone],
)}
style={{ x: reduce ? x : sx, y: reduce ? y : sy }}
animate={{ scale: visible ? 1 : 0, opacity: visible ? 1 : 0 }}
transition={{ duration: reduce ? 0 : 0.15 }}
initial={false}
>
{label}
</motion.span>
</div>
);
}
export default CursorFollower;
modules/brutalist/effect/cursor-follower/index.tsx
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Region content. |
| label | string | "Ship it →" | Sticker text. |
| tone | "primary" | "accent" | "secondary" | "primary" | Sticker color. |
| className | string | — | Classes on the region. |
Other micro interaction variants in Brutalist
Click Burst
Micro-interaction wrapper that fires a burst of outlined square shards in DA colors from the click point (or from the center on Enter/Space), spinning outward in stepped motion.
Shake Feedback
Error micro-interaction wrapper: shakes its content horizontally in hard steps and flashes a thick danger outline each time shakeKey changes (wrong password, invalid code). Children keep their state.
Stack Hover
Micro-interaction wrapper: colored slabs hidden behind a card or button fan out diagonally on hover and keyboard focus, in stepped motion, like a stack of printed sheets. Pure CSS.
Micro interaction in other art directions
Glare Card
Glass card where a soft specular glare and a luminous primary rim follow the mouse pointer (CSS variables, masked border), like light sliding over real glass.
Gradient Border
Highlight wrapper with a rotating conic-gradient border (primary → accent → pink) and optional soft glow around a frosted inner surface. Pure CSS via a registered @property angle.
Liquid Button
Frosted pill button that fills with a wavy primary→accent liquid rising from the bottom on hover or keyboard focus while the label turns white. Pure CSS.
Tilt Card
Frosted card that tilts in 3D toward the pointer on a spring while a specular glare slides across the glass; eases back flat on leave. Motion values only, no re-render per move.
Border Beam
Card wrapper with a short primary-to-accent light beam travelling around its rounded 1px border (conic gradient + animated @property angle); always on or only on hover/focus.
Magnetic Button
Wrapper that pulls its child toward the mouse pointer on hover with a spring (inner content moves a little more for depth) and snaps back on leave.
Ripple Press
Subtle press ripple expanding from the pointer inside any rounded child (button, card), in the current text color or primary tone, fading out quickly without blocking clicks.
Spotlight Card
Card micro-interaction: a soft indigo radial light follows the pointer inside the card while the 1px border brightens under the cursor. CSS variables only, no React re-render per move.
Copy Email
Big email address that copies itself on click: the address swaps to “Copied to clipboard” with a signal dot for 1.8s via a vertical slide; announced to screen readers.
Cursor Label
Hover area where a small ink pill with a mono label (“View ↗”) follows the pointer with slight lag; the native cursor stays visible. Mouse only; nothing on touch.
Invert Hover
Row/button whose ink fill wipes in from the left on hover or focus while the text inverts (mix-blend-difference) — the signature interaction for index lists.
Square Switch
Square toggle: a hairline rectangle with a square knob that snaps across with the emphasized ease, filling ink when on; mono ON/OFF state label; role=switch.
Copy Feedback
Copyable mono field (API key, IBAN, invoice link): click copies, the icon cross-fades to a green check and a “Copied” tooltip pops for 1.6s; announced to screen readers.
Press Ripple
Button with a restrained material-style ripple: a soft circle expands from the press point and fades, plus a 1px press-down; disabled with reduced motion.
Spotlight Border
Card whose 1px border lights up in primary around the pointer (a radial highlight follows the cursor along the edge) with a faint inner glow; plain bordered card on touch.
Toggle Save
Settings row with an auto-saving switch: the knob slides, an inline status shows a spinner “Saving…” then a green “Saved” check that fades after 2s (aria-live).
Breathe Button
Calm primary CTA that slowly “breathes”: a soft sage halo expands and fades around the pill every few seconds; pauses on hover and with reduced motion. Pure CSS.
Leaf Like
Appreciation button: pressing fills the leaf with sage, bumps it with a soft overshoot and releases three small leaves that float up and fade; counter updates.
Sprout Progress
Progress bar with a plant riding the fill: a rounded sage track whose leading edge carries a sprout that grows leaves at 33% and 66% and blooms at 100%.
Water Ripple
Surface that answers a tap like still water: three concentric sage rings spread from the pointer and fade. Wrap any card or panel.
Check Burst
Satisfying task checkbox: the round box fills mint with a bouncy check and a ring of pastel dots bursts outward when ticked; the label strikes through.
Heart Like
Like button: the heart pops with a springy scale, fills red and throws a ring of tiny hearts; the count ticks and the pill tints.
Hover Lift
Card wrapper that lifts, tilts a couple of degrees and grows a soft wide shadow on hover or keyboard focus-within, with a springy ease.
Squish Button
Chunky “gummy” pill button with a flat darker bottom lip that squishes down (scales wider/shorter, lip disappears) when pressed and springs back. CSS only.