thisdesign.space

CSS · 300 ms

Magnetic hover

An element that pulls toward the cursor when nearby, snaps back when it leaves.

Listen for pointermove on the element. Compute the cursor's offset from the element centre and apply it as a transform — scaled down (e.g. 0.3) so the element follows but doesn't track 1:1. On pointerleave, transition back to zero. The factor (we use 0.35) is the whole game: 0.1 feels lifeless, 0.6 feels gimmicky. 0.35 reads as tactile.

Demo

Live demo

Move your cursor near the button — it follows at 35% of the offset.

The demo runs the same CSS code shown below — replay to retrigger.

Code

Pointermove handler — apply translate scaled to a fraction of the cursor offset.js

const el = document.querySelector('.magnetic');
const FACTOR = 0.35;

el.addEventListener('pointermove', (e) => {
  const r = el.getBoundingClientRect();
  const cx = r.left + r.width  / 2;
  const cy = r.top  + r.height / 2;
  const dx = (e.clientX - cx) * FACTOR;
  const dy = (e.clientY - cy) * FACTOR;
  el.style.transform = `translate(${dx}px, ${dy}px)`;
});

el.addEventListener('pointerleave', () => {
  el.style.transform = 'translate(0, 0)';
});

Spring back is just a transition — no JS needed for the return.css

.magnetic {
  transition: transform 300ms cubic-bezier(0.2, 0.9, 0.2, 1);
}