Small physical cues make interfaces feel alive. Each element is pulled toward the pointer inside a radius and released onto a damped spring.
Every element measures the pointer against its own centre. Inside half-size plus the field radius, the target offset is the delta scaled by strength, and the inner label gets a further share for parallax; outside, the target returns to zero. The offsets are written to framer-motion springs, so the release is a damped oscillation instead of a tween.
const x = useSpring(0, { stiffness, damping });
const lx = useSpring(0, { stiffness, damping });
const onPointer = (pointer) => {
const rect = wrap.getBoundingClientRect(); // wrapper never moves
const dx = pointer.x - (rect.left + rect.width / 2);
const dy = pointer.y - (rect.top + rect.height / 2);
const reach = Math.max(rect.width, rect.height) / 2 + radius;
if (Math.hypot(dx, dy) < reach) {
x.set(dx * strength * 0.5); // body
lx.set(dx * strength * 0.25); // label, extra parallax
} else {
x.set(0);
lx.set(0);
}
};