Skip to content
Black FridayBlack Friday launch price: Lifetime $149 until 30 Nov. Regular price $299 from 1 Dec.ends in 15d 02:34:23Get Lifetime →
loopkit
proNew208 lines · framer-motion

Cursor Magnetic Buttons

Buttons that lean in before you even click. A bounded demo panel of sample buttons that spring toward the pointer once it's within range, with the label pulling slightly harder than the button shell for a two-layer magnetic feel, then snap back to rest when the pointer moves away.

Use it for
A nav, toolbar or CTA row on a product or portfolio site that wants its buttons to feel physically responsive instead of just having a hover colour change.
Themes
Light + dark
License
Commercial use
Live
Pointer field

Buttons with a pull

Move your pointer near a button; it leans toward you, then springs back.

tsx
"use client";

import * as React from "react";
import { motion, useReducedMotion, useSpring } from "framer-motion";
import { cn } from "@/lib/utils";

function useReducedMotionSafe() {
  const prefersReduced = useReducedMotion();
  const [mounted, setMounted] = React.useState(false);
  React.useEffect(() => setMounted(true), []);
  return mounted ? !!prefersReduced : false;
}

/**
 * A bounded demo panel holding 3–4 sample buttons. While the pointer is
 * inside the panel and within `radius` of a button, that button (and its
 * label, at a slightly stronger pull for a two-layer feel) springs toward
 * the pointer, falling off with distance and clamped to a max offset; it
 * snaps back on a spring once the pointer leaves its field or the panel.
 * Tracking is scoped to the panel element (never `window`) and the whole
 * effect falls back to perfectly static, clickable buttons on touch devices
 * and under `prefers-reduced-motion`.

The rest is in Pro.

Full source, the rebuild prompt and registry install unlock with Pro or Lifetime.

Get Pro

How it behaves

  • →On every pointer move inside the panel, each button's centre distance to the pointer is measured; inside radius it's pulled toward the pointer with strength falling off quadratically with distance, clamped to a max offset.
  • →The button shell and its inner label+icon move on independent springs at different strengths, so the label visibly leads the shell for a layered magnetic feel.
  • →Buttons spring back to their resting position on their own once the pointer leaves their field, and all of them reset together when the pointer leaves the panel.
  • →strength scales the pull and both max offsets; radius sets how far the field reaches; buttonCount renders 3 or 4 of the sample buttons; shape switches between pill (label + icon) and circle (icon only).
  • →Pointer tracking is bound to the panel element via onPointerMove/onPointerLeave — never window — so nothing moves outside the bounded box.
  • →On touch devices (pointer: coarse or hover: none) and under prefers-reduced-motion, the pointer handler is a no-op: buttons sit still and are perfectly clickable, with the normal cursor.

Accessibility

  • ✓Every button is a real `<button>` the whole time; the magnetic pull is only a `transform` via framer-motion `style`, so click handling, keyboard focus and activation (Enter/Space) all keep working unchanged.
  • ✓`:focus-visible` outlines render on the button itself, so they stay anchored to it even while it's magnetically offset.
  • ✓Icons are `aria-hidden`; each button's visible label is its only accessible name.
  • ✓Honours prefers-reduced-motion.