Magnetic Button Effect — CSS & JavaScript Animation with Source Code
The Magnetic Button is a premium hover interaction where the button physically moves toward the user’s cursor as they hover near it, creating a magnetic attraction feeling. This effect is widely used on high-end agency websites and design portfolios. It uses vanilla JavaScript to track the cursor’s position relative to the button’s center and applies CSS transform: translate() proportionally.
How It Works
A mousemove event listener on the button calls getBoundingClientRect() to get the button’s position and dimensions. The cursor’s offset from the button’s center is calculated as x = clientX - rect.left - width/2 and y = clientY - rect.top - height/2. These values are scaled down (multiplied by 0.28 for the button, 0.15 for the inner text) and applied as transform: translate(xpx, ypx). On mouseleave, the transform resets to empty string, triggering the CSS transition back to origin.
Use Cases
- Call-to-action buttons on landing pages
- Navigation items on premium agency or portfolio sites
- Interactive menu items and pill buttons
- Icon buttons with subtle depth cues
How to Customize
- Increase magnetic pull: Increase the multiplier from
0.28to0.45or higher - Change button shape: Remove
border-radius: 100pxfor a square button - Add a fill animation on hover: The
.mag-btn:hoverCSS already handles background/color change — customize these values - Add a ripple on click: Append a ripple
<span>element onclickevents with a CSS keyframe scale animation
Frequently Asked Questions
Why does the button text move less than the button itself?
The inner .mag-text span uses a 0.15 multiplier compared to the button’s 0.28. This creates a parallax effect where the text lags slightly behind the button motion, adding depth and a premium feel. Both travel in the same direction.
Why must .mag-text have pointer-events: none?
Without this, hovering over the text element fires mouseleave on the button (since it’s technically a child boundary), causing jitter. Setting pointer-events: none makes the text invisible to mouse events so all events pass through to the parent button.
Does this work on touchscreens?
mousemove does not fire on touch devices. The button will display its static styles without movement, which is the correct graceful degradation. No broken behavior occurs on mobile.