Hover✨ Premium

Magnetic Pull

The element translates toward the mouse (factor 0.2 of the center-cursor distance) and snaps back on mouseleave — pure JavaScript magnetic attraction, no library, 400 bytes.

JSMousePhysics

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →

3 usage examples

'Join the waitlist' CTA button — startup landing page — Magnetic Pull example 1

① 'Join the waitlist' CTA button — startup landing page

WhenCTA button centred in the hero; it moves slightly toward the cursor to physically invite a click, with a 32 px reaction zone around it (padding).
WhyA button that comes toward you creates a more subtle implicit invitation than a colour change or drop shadow: it is a physical affordance in a digital interface.
SettingsButton 220×52 px centred, pink-red gradient, 16 px padding around the root to absorb the 20% displacement, near-black background.
Social media icons — footer of an open-source studio — Magnetic Pull example 2

② Social media icons — footer of an open-source studio

WhenFive social link icons (GitHub, X, LinkedIn, Dribbble, Figma) in a row in the footer; each icon moves toward the cursor independently.
WhyIcons that advance toward the cursor in a quiet footer make the zone more lively and signal the links without adding a hover background or border.
SettingsFive 44×44 px data-magnetic-box instances, border-radius 10 px, transparent background at rest, 6 px padding around each root.
Team member cards — 'About' section — Magnetic Pull example 3

③ Team member cards — 'About' section

WhenThree team member cards side by side; each card moves toward the cursor as if wanting to be consulted, inviting a click to view the full profile.
WhyA card that moves toward you creates a more natural implicit invitation to interact than highlighting or enlarging it.
SettingsThree card-size data-magnetic-box instances, 10 px padding around each root, dark 1a1f2e gradient, avatar + name + role.

How it works

The .fx-0358-root root centers a .hover-box.hover-magnetic block of 200 × 200 px with the data-magnetic-box attribute. The element has a pink-red gradient background and a transform .2s ease-out transition to smooth micro-jitter between mousemove events.

The script searches all .fx-0358-root [data-magnetic-box] and attaches two listeners. On mousemove: it computes the offset between the cursor position and the element's center (clientX − rect.left − width/2) and applies transform: translate(a×0.2 px, b×0.2 px) — the element moves 20% of the center-cursor distance toward the pointer. On mouseleave: transform: translate(0,0) returns the element to its original position (the CSS transition smooths the return).

Under prefers-reduced-motion: reduce, the script skips mousemove and the element stays still. Measured in Chromium 151: attraction visible, automatic return, two copies on a page — each element tracks its own mouse.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, the attraction is disabled and the element stays still. mouseleave remains active to reset position if the mode is enabled after a hover.
  • Keyboard and touch: the attraction only follows mousemove — not reproducible with keyboard or touch. The element remains functional (click, focus) without the magnetic effect. To signal keyboard focus, add :focus-visible { outline: 2px solid #6366f1; } in your page CSS.
  • Content: the effect translates the entire element. On tight layouts, the 20% displacement may move the element outside its allocated space — increase padding around the zone to absorb the shift.
  • Clickable zone: since the element moves, the click target moves too. Avoid very large displacement zones to avoid disorienting users.

Browser compatibility

400 bytes of JavaScript (mousemove, mouseleave, getBoundingClientRect, matchMedia), no dependencies. CSS: transform, transition. Measured in Chromium 151: precise attraction, smooth return, two independent copies. Works in all modern browsers.

Chrome 36+✓ Full (measured)
Firefox 16+✓ Full
Safari 9+✓ Full
Edge 79+✓ Full (Chromium engine)
Mobile iOS✓ Not applicable (no mouse, attraction does not trigger)
Android Chrome 36+✓ Not applicable (no mouse, attraction does not trigger)

Without JavaScript, the element stays static. Without transform or transition, the attraction does not work.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0358-root">
  <div class="hover-box hover-magnetic" data-magnetic-box="">
    <span>…</span>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Attraction factor (JS — the 0.2 in .2*a and .2*l) 20% of center-cursor distance 0.1 makes the attraction very subtle (10%); 0.3 makes it more pronounced. Above 0.4, the element may leave its container on edge cursors.
CSS transition duration (transition: transform .2s ease-out) 200 ms .1s makes tracking more reactive; .4s adds more inertia. ease-out is recommended to absorb event artifacts.
Background color (CSS — background of .hover-magnetic) linear-gradient(135deg, #ec4899, #f43f5e) Replace with any background (solid color, gradient, image). The background does not change the attraction — only JS controls the transform.
Listening zone (HTML — data-magnetic-box) The .hover-magnetic element itself Put data-magnetic-box on a larger parent container to trigger attraction before the cursor touches the visible element.
Return to rest (JS — mouseleave) translate(0,0) The CSS transition (transform .2s ease-out) animates the return. To remove the return animation (instant snap back), remove the transition property from .hover-magnetic.

FAQ

No: it relies on mousemove which is not emitted on an ordinary touch. On mobile, the element stays still. The effect is designed for cursor interfaces.
Yes if the container has overflow: hidden removed. Without overflow: hidden on the parent, the element may overlap neighbors. Provide padding around the attraction zone (at least 20% × factor × element size).
Cap the displacement values in JS: replace .2*a with Math.max(-30, Math.min(30, .2*a)) (±30 px limit) and do the same for l.