Magnetic Button
rivelle/magnetic-buttonFreeComponent
A tactile action that leans toward the pointer and snaps back.
Install it
npx shadcn@latest add https://rivelle.dev/r/magnetic-button.jsonSource
1"use client";23import * as React from "react";45import { cn } from "@/lib/utils";67type MagneticButtonProps = React.ComponentProps<"button"> & {8 strength?: number;9};1011function MagneticButton({12 children,13 className,14 onPointerLeave,15 onPointerMove,16 strength = 18,17 style,18 type = "button",19 ...props20}: MagneticButtonProps) {21 const frame = React.useRef<number | null>(null);2223 const move = (24 element: HTMLButtonElement,25 x: number,26 y: number,27 glowX: number,28 glowY: number,29 ) => {30 if (frame.current !== null) cancelAnimationFrame(frame.current);31 frame.current = requestAnimationFrame(() => {32 element.style.setProperty("--magnetic-x", `${x}px`);33 element.style.setProperty("--magnetic-y", `${y}px`);34 element.style.setProperty("--magnetic-glow-x", `${glowX}%`);35 element.style.setProperty("--magnetic-glow-y", `${glowY}%`);36 });37 };3839 React.useEffect(40 () => () => {41 if (frame.current !== null) cancelAnimationFrame(frame.current);42 },43 [],44 );4546 return (47 <button48 className={cn(49 "group relative inline-flex min-h-12 items-center justify-center gap-2 overflow-hidden rounded-full bg-foreground px-6 text-sm font-semibold text-background shadow-[0_18px_50px_-24px_var(--foreground)] outline-none transition-[transform,box-shadow] duration-200 ease-out hover:shadow-[0_24px_65px_-28px_var(--foreground)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background active:scale-[.98] disabled:pointer-events-none disabled:opacity-50 motion-reduce:transform-none motion-reduce:transition-none",50 className,51 )}52 data-slot="magnetic-button"53 onPointerLeave={(event) => {54 move(event.currentTarget, 0, 0, 50, 50);55 onPointerLeave?.(event);56 }}57 onPointerMove={(event) => {58 if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {59 const bounds = event.currentTarget.getBoundingClientRect();60 const relativeX = (event.clientX - bounds.left) / bounds.width;61 const relativeY = (event.clientY - bounds.top) / bounds.height;62 const x = (relativeX - 0.5) * strength * 0.72;63 const y = (relativeY - 0.5) * strength * 0.72;64 move(event.currentTarget, x, y, relativeX * 100, relativeY * 100);65 }66 onPointerMove?.(event);67 }}68 style={69 {70 "--magnetic-glow-x": "50%",71// … truncated
What it pulls in
Other registry items
Files it writes
More from rivelle
All 83 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | rivelle | Components | Free | 2 deps | |
| Alertalert | rivelle | Components | Free | 1 dep | |
| Alert Dialogalert-dialog | rivelle | Components | Free | 1 dep | |
| Aspect Ratioaspect-ratio | rivelle | Components | Free | 1 dep | |
| Avataravatar | rivelle | Components | Free | 1 dep | |
| Badgebadge | rivelle | Components | Free | 2 deps | |
| Breadcrumbbreadcrumb | rivelle | Components | Free | 2 deps | |
| Buttonbutton | rivelle | Components | Free | 2 deps |
