Installation
Registry — recommended
Copies the component into your configured shadcn UI directory and installs Motion.
npx shadcn@latest add https://suluu.site/r/magnet-pull.jsonnpm package
For centralized upgrades, install the optional ESM package and configure Tailwind source detection as shown below.
pnpm add suluu motionUsage
MagnetPull is a native button. Everything you would pass to one — click handlers, form attributes, ARIA — works unchanged.
import { MagnetPull } from "@/components/ui/magnet-pull"
export function Hero() {
return (
<MagnetPull onClick={() => startTrial()}>
Get started
</MagnetPull>
)
}// app/globals.css
@import "suluu/styles.css";
@source "../node_modules/suluu/dist";
// component.tsx
import { MagnetPull } from "suluu/magnet-pull"
export function Hero() {
return <MagnetPull onClick={() => startTrial()}>Get started</MagnetPull>
}Tuning the field
radius sets how far the field reaches and strength sets how far the button travels. Keep the radius comfortably smaller than the gap to neighboring controls, so two magnets never overlap.
// A wide, slow, unmistakable magnet for a hero CTA.
<MagnetPull
contentStrength={0.6}
motionIntensity="expressive"
radius={200}
strength={0.4}
>
Get started
</MagnetPull>
// A tight, barely-there magnet for dense toolbars.
<MagnetPull
contentStrength={0.18}
motionIntensity="subtle"
radius={48}
strength={0.1}
>
Save
</MagnetPull>In context
Use the magnetic pull sparingly on the one action that carries a page forward.
Built for small teams
Turn the next idea into something real.
A focused workspace for shipping thoughtful product work together.
Props
MagnetPull also accepts native button attributes and forwards its ref to the button element.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Content rendered in the parallax layer. |
| radius | number | 120 | Pixels beyond the button bounds where the pull engages. |
| strength | number | 0.2 | Fraction of the cursor offset the surface travels. |
| contentStrength | number | 0.32 | Total fraction the content travels, creating the parallax. |
| motionIntensity | "subtle" | "default" | "expressive" | "default" | Spring character of the pull and the release. |
| onEngagedChange | (engaged) => void | — | Runs when the cursor enters or leaves the field. |
| disabled | boolean | false | Disables the button and the magnetism. |
| className | string | — | Class name applied to the button. |
| type | "button" | "submit" | "reset" | "button" | Native button type. |
Theming
Override these variables in your light and dark theme scopes. The registry installs the defaults automatically.
--suluu-magnet-backgroundButton surface.--suluu-magnet-foregroundLabel and icon color.--suluu-magnet-hoverSurface while hovered.--suluu-magnet-ringKeyboard focus ring.--suluu-magnet-offsetColor behind the focus ring offset.--suluu-magnet-shadowResting depth under the button.The button also exposes data-magnet-engaged, so you can style the engaged state directly with data-[magnet-engaged=true]:shadow-lg.
Accessibility
MagnetPull renders a plain native button and adds no ARIA of its own. Keyboard users get a focus-visible ring and deliberately no magnetism — there is no cursor to follow, and a control that moves when you tab to it is disorienting.
The effect only runs on devices matching (hover: hover) and (pointer: fine), so touch devices never see it. Under prefers-reduced-motion the pointer listener is never attached at all — the button is genuinely inert rather than animated at zero duration — while staying fully clickable.