magnetic-cursor-interactions
Magnetic Cursor Interactions
When to use this
- Building hero CTAs, nav links, or icon buttons that need to feel alive and responsive to proximity.
- Adding subtle pull effects to cards or interactive surfaces that respond before the user actually hovers.
- Creating dock-style icon magnification where neighbors scale based on distance.
- Implementing floating action buttons or toolbars with magnetic snapping.
- Do NOT use this for drag-and-drop interactions; see
drag-drop-dnd-kitinstead.
Mental model
A magnetic element defines a pull radius (typically 1.5-2x its own dimensions). On every mousemove, you compute the distance from the cursor to the element center. If the cursor is inside the pull radius, you calculate a normalized strength (0 at edge, 1 at center) and translate the element toward the cursor by that strength times a maximum displacement.
The displacement itself is the vector from center to cursor, scaled down. You never move the element all the way to the cursor; you move it a fraction (typically 30-40% of the distance). This fraction is the "pull strength." The return to rest uses a lerp or spring, not a snap, so the element drifts back smoothly when the cursor leaves.
Touch devices get none of this. There is no hover on touch. Do not attempt to fake it with touchmove; it creates a janky drag-like behavior that confuses users. Detect touch with matchMedia('(hover: hover)') and skip the entire system.
The animation runs on transform: translate() only, which is compositor-safe. Never magnetic-pull left/top/margin. Use requestAnimationFrame for the lerp loop and cancel it when no elements are active to avoid idle CPU burn.