Magnetic Button — Free HTML CSS JS Hover Snippet

Magnetic Button · Buttons · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

getBoundingClientRect gives center coordinates for accurate mouse offset calculation
dx/dy vector from cursor to button center, distance via Pythagorean theorem
(1 - dist/maxDist) * strength falloff — pull fades as cursor moves away
data-strength attribute per button — different intensities without code changes
maxDist = 1.2× the larger dimension — effective radius scales with button size
mouseleave snaps back to translate(0,0) via elastic cubic-bezier ease-out
Works on any button element — add three lines of JS and one CSS transition
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Magnetic Button — getBoundingClientRect Vector Math & distance-based translateX/Y

Screenshot of the Magnetic Button snippet rendered live

A magnetic button physically pulls toward the cursor as it hovers near — the button moves to meet the mouse, then snaps back when the mouse leaves. The effect makes CTAs feel tactile and interactive, turning a static button into a micro-interaction that communicates responsiveness — much like the click ripple. It is used on premium agency sites, creative portfolios, and any interface aiming for a distinctive, physical UI quality.

The vector math

The JS reads the button's position with getBoundingClientRect() to get cx (center x) and cy (center y). The mouse offset from center is dx = e.clientX - cx and dy = e.clientY - cy. The distance from the center is Math.sqrt(dx*dx + dy*dy).

A maximum effective distance is set to Math.max(rect.width, rect.height) * 1.2 — the button only reacts when the cursor is within 1.2× the button's larger dimension. A falloff factor scales with (1 - dist / maxDist) * strength — the closer the cursor, the stronger the pull. The final offset is (dx * factor / maxDist, dy * factor / maxDist) applied as translateX/Y.

The data-strength attribute

Each button has a data-strength attribute (defaulting to 30). Higher values make the button move more; lower values make it subtler. The two demo buttons use different strengths to show the range.

The snap-back

On mouseleave, btn.style.transform = 'translate(0,0)' resets the position. The CSS transition: transform 0.15s cubic-bezier(0.23,1,0.32,1) applies an elastic ease-out so the snap-back has a slight overshoot quality.

Adding to any existing button

Add data-strength="30" to any button — including a gradient button — add the three event listeners from the JS, and ensure the button has transition: transform 0.15s in its CSS. No other changes needed.

The vector math

On mousemove inside the magnetic zone, two calculations run: (1) distance = Math.hypot(dx, dy) where dx and dy are the cursor offset from the button centre. (2) If distance < strength radius, apply a force: x += (cursorX - buttonCentreX) * 0.3; y += (cursorY - buttonCentreY) * 0.3. This moves the button 30% of the distance toward the cursor. The button position is updated via transform: translate(x + 'px', ' + y + 'px'), and lerp smoothing (x += (targetX - x) * 0.12) eases the movement each requestAnimationFrame frame.

The spring return

On mouseleave, the target position resets to (0, 0). The lerp loop continues running until x and y are close enough to zero (abs < 0.5), then it stops. This creates the elastic return animation — the button springs back to its natural position with decreasing velocity, exactly like a physical magnet being released.

Configuring the attraction strength

Increase the 0.3 multiplier for stronger attraction (button moves more per pixel of cursor distance). Decrease for subtler effect. The strength radius (typically 80-120px) controls how close the cursor must be to trigger attraction. Smaller radius = more precise interaction; larger = more ambient magnetic feel.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to re-derive the falloff formula from scratch. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the factor calculation multiplies (1 - dist / maxDist) by strength before scaling dx and dy, and why maxDist is defined relative to the button's own width and height rather than a fixed pixel constant. The same assistant can help optimize it, for instance asking whether setting btn.style.transform directly on every mousemove event risks layout thrash compared to batching the write inside a requestAnimationFrame callback. It is also useful for extending the effect: ask it to add a lerped spring-back instead of the instant CSS-transition snap, make the button's inner label counter-rotate slightly for a parallax feel, or apply the same technique to an icon inside a card rather than a button. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "magnetic button" hover effect in plain HTML, CSS, and JavaScript with no libraries.

Requirements:
- One or more button elements, each carrying a data-strength attribute controlling how far that specific button can be pulled, defaulting to a sensible value if the attribute is missing or unparsable.
- On mousemove over a button, compute the button's center point using getBoundingClientRect (not a hardcoded position), then compute the cursor's offset from that center as a vector (dx, dy) and its straight-line distance using the Pythagorean theorem.
- Define an effective interaction radius relative to the button's own measured dimensions (e.g. the larger of its width or height times a multiplier), not a fixed pixel value, so bigger buttons naturally get a bigger magnetic zone.
- Only apply a transform when the cursor is within that effective radius; the pull strength must fall off smoothly from full strength at the center to near zero at the radius edge, using a formula proportional to (1 - distance / radius).
- Apply the computed pull as a CSS transform: translate on the button element (not on an inner wrapper), scaled by the per-button strength attribute, and give the button a CSS transition on the transform property so that both the pull and the reset below feel eased rather than instant.
- On mouseleave, reset the button's transform back to no translation, relying on the existing CSS transition for the snap-back rather than manually animating the reset in JavaScript.
- The label/text inside the button must not itself intercept pointer events in a way that breaks the parent button's mousemove tracking.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Hover near each buttonMove the cursor near the buttons in the preview without clicking. Each button pulls toward the cursor based on its data-strength value.
  2. 2
    Change the pull strengthIn the HTML panel, update the data-strength attribute on each button. Higher values (40-60) create stronger pull; lower values (10-20) are more subtle.
  3. 3
    Change button stylesUpdate the background gradient, border-radius, font-size, and padding in the CSS panel to match your design.
  4. 4
    Add to an existing buttonAdd data-strength="30" to your button, copy the JS mousemove and mouseleave listeners, and add transition: transform 0.15s to its CSS.
  5. 5
    Adjust the effective radiusIn the JS panel, change the 1.2 multiplier in maxDist to control how far the cursor must be before the button starts reacting.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Hero CTA buttons on premium landing pages
Make the primary CTA physically attract the cursor for an immediate tactile quality. The magnetic pull makes users notice the button more than a hover colour change alone.
Creative agency and portfolio CTAs
Magnetic buttons are a signature interaction on award-winning agency sites. Add to any portfolio CTA to communicate that the interface is crafted with intent.
Learn 2D vector math in the browser
The snippet uses distance calculation and vector scaling — real physics math. Edit the strength and radius values in the JS panel to see how each parameter controls the attraction behaviour.
Prototype premium interactive UIs
Use the magnetic button to prototype the feel of a high-end interactive interface. Test whether the effect fits your brand at different strength values.
Add to any existing button in 3 steps
Add data-strength, copy the JS listeners, add a CSS transition. No wrapper elements or extra HTML needed.
Product launch and waitlist CTAs
A magnetic signup button on a product launch page draws attention and communicates that every detail has been considered.

Got questions?

Frequently Asked Questions

The mousemove handler calculates the vector from the button centre to the cursor (dx, dy) using getBoundingClientRect. It computes the distance and a falloff factor that decreases from strength to 0 as the cursor moves from the centre to the edge of the effective radius. This factor scales dx and dy to produce the translateX/Y offset.

data-strength controls the maximum pixel offset the button moves. A value of 30 moves the button up to 30px toward the cursor at maximum proximity. Change it per button to create different pull intensities across your UI.

The effective radius is Math.max(rect.width, rect.height) * 1.2. Change 1.2 to a larger value like 2.0 to start attracting the cursor from further away, or to 0.8 to require the cursor to be nearly on the button before reacting.

Add data-strength="30" to your button HTML. Copy the mousemove and mouseleave event listeners from the JS panel. Add transition: transform 0.15s cubic-bezier(0.23,1,0.32,1) to your button CSS. No additional markup needed.

The CSS transition: transform 0.15s cubic-bezier(0.23,1,0.32,1) applies an elastic ease-out to all transform changes including the reset to translate(0,0) on mouseleave. The cubic-bezier values create a slight overshoot and bounce.

Yes. Click "JSX" to download a React component. In React, attach the mousemove and mouseleave handlers via onMouseMove and onMouseLeave props. Track the transform in useState or apply it directly to the element via useRef to avoid re-render overhead.