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.
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



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.mouseleaveremains 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.
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):
<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 / property | Default | Effect |
|---|---|---|
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
mousemove which is not emitted on an ordinary touch. On mobile, the element stays still. The effect is designed for cursor interfaces.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)..2*a with Math.max(-30, Math.min(30, .2*a)) (±30 px limit) and do the same for l.