Physics Buttons

v1.1.0
FreeButtonsInteractiveTailwind

A suite of four physics-driven action buttons — gooey magnetic (slime), elastic bezier, jelly, and impact — built on GSAP spring physics with style presets and deep prop customization.

Physics Buttons

Installation

npx vectorvesper add physics-buttons

Run npx vectorvesper init once first to set up your project.

Dependencies

npm install gsap @gsap/react
  • gsap
  • @gsap/react

Also requires tailwindcss to be configured in your project.

Usage

Import

import { SlimeButton, ElasticButton, JellyButton, ImpactButton } from "@/components/vv/physics-buttons";

Usage

<SlimeButton />
<ElasticButton />
<JellyButton />
<ImpactButton />

Client-only component — disable SSR

This component requires browser APIs and must be rendered client-side only.

import dynamic from "next/dynamic";

const JellyButton = dynamic(
  () => import("@/components/vv/physics-buttons").then((m) => m.JellyButton),
  { ssr: false }
);
Tailwind CSS must be configured in your project for styles to apply correctly.

Props

A suite of four GSAP spring-physics action buttons — SlimeButton (gooey magnetic), ElasticButton (bezier stretch), JellyButton (rounded-rect ripple), and ImpactButton (fall, shatter & rejoin). Each ships style presets via the variant prop and exposes deep prop overrides. Import any subset from @/components/vv/physics-buttons.

Shared Props

Common to all exported components in this suite.

PropTypeDefaultDescription
children*React.ReactNodeButton label or content.
buttonColorstring— (per preset)Primary fill color. Falls back to the active variant's preset color.
clickColorstring"#FFFFFF"Color flashed on click, then eased back to buttonColor.
onClick() => voidClick handler.
disabledbooleanfalseDisable interaction (no animation, dimmed, not activatable).
type"button" | "submit" | "reset""button"Native button type — each component renders a real <button>.
aria-labelstringAccessible label, useful when children are icon-only.
classNamestring""Extra CSS classes.
styleReact.CSSProperties{}Inline style overrides.

<SlimeButton />

Gooey magnetic button: a fluid body with droplets that attract the cursor and splash on click.

PropTypeDefaultDescription
variant"slime" | "orbit" | "square""slime"Style preset (see below).
rangenumber150Proximity in px within which the button attracts the cursor.
strengthnumber0.3Multiplier for body translation toward the cursor.
particleColorstring= buttonColorColor of the gooey droplets.
particleCountnumber— (per preset)Number of fluid droplets.
gravitynumber— (per preset)Downward acceleration on droplets while active.
haloSpeednumber— (per preset)Idle orbit rotation speed (used by the `orbit` preset).
borderRadiusstring"9999px"Body corner radius (`square` preset uses `16px`).
minWidthstring"160px"Minimum body width (CSS length).
heightstring"56px"Body height (CSS length).
releaseTimeSecnumber2.0Delay before droplets snap back after the cursor leaves.
recoilIntensitynumber0.22Vertical wobble on release (splash recoil).
stdDeviationnumber8.5Gaussian blur for the gooey filter (lower = crisper).
squishIntensitynumber-0.35Vertical squish impulse on click.

Presets

PresetDescriptionKey Overrides
"slime"Lime, gravity-fed gooey drip.
buttonColor="#ADFA1D"gravity=0.55particleCount=8
"orbit"Blue droplets orbiting a still body.
buttonColor="#3B82F6"haloSpeed=2.2gravity=0particleCount=6
"square"Pink, square-boundary droplet field.
buttonColor="#EC4899"borderRadius="16px"gravity=0particleCount=8

<ElasticButton />

Bezier-edged button whose border stretches toward the cursor and plucks back on click.

PropTypeDefaultDescription
variant"elastic" | "outlined" | "tension""elastic"Style preset (see below).
widthnumber220Body width in px.
heightnumber60Body height in px.
rangenumber140Proximity in px that triggers edge elasticity.
borderColorstring— (per preset)Stroke color of the elastic edge.
borderWidthnumber— (per preset)Stroke width in px.
maxStretchnumber28Max edge displacement on proximity.
pluckForcenumber— (per preset)Impulse applied to the edges on click.

Presets

PresetDescriptionKey Overrides
"elastic"Solid red, soft pluck.
buttonColor="#FF1800"borderWidth=0pluckForce=38
"outlined"Transparent body, cyan stroke.
buttonColor="transparent"borderColor="#00F0FF"borderWidth=2pluckForce=50
"tension"Cyan, stiffer/snappier spring.
buttonColor="#00F0FF"pluckForce=60

<JellyButton />

Rounded-rect jelly blob that deforms toward the cursor and ripples outward on click.

PropTypeDefaultDescription
variant"jelly" | "viscous""jelly"Style preset (see below).
widthnumber200Body width in px.
heightnumber56Body height in px.
radiusnumber14Corner radius of the rounded-rect blob.
rangenumber130Proximity in px for hover deformation.
maxStretchnumber— (per preset)Max hover deformation distance toward the cursor.
rippleForcenumber— (per preset)Impulse injected into the blob on click.

Presets

PresetDescriptionKey Overrides
"jelly"Violet, bouncy default.
buttonColor="#8B5CF6"maxStretch=26rippleForce=35
"viscous"Emerald, slow and gooey.
buttonColor="#10B981"maxStretch=45rippleForce=20

<ImpactButton />

Drops on click, shatters into gooey chunks with a splat, then coalesces and springs back.

PropTypeDefaultDescription
variant"impact" | "drop" | "sticky""impact"Style preset (see below).
widthnumber200Body width in px.
heightnumber56Body height in px.
fallDistancenumber— (per preset)Distance in px the button falls before shattering.
impactDelayMsnumber— (per preset)Time the pieces stay shattered before rejoining.
particleColorstring= buttonColorColor of the splat droplets.
particleCountnumber— (per preset)Number of droplets emitted on impact.
minRadiusnumber5Minimum splat droplet radius.
maxRadiusnumber11Maximum splat droplet radius.
borderRadiusstring"16px"Body corner radius.

Presets

PresetDescriptionKey Overrides
"impact"Lime, short fall + quick rejoin.
buttonColor="#A3E635"fallDistance=60impactDelayMs=350particleCount=10
"drop"Red, tall fall + heavy splatter.
buttonColor="#EF4444"fallDistance=150impactDelayMs=600particleCount=18
"sticky"Blue, long pause before rejoin.
buttonColor="#3B82F6"impactDelayMs=1000

Examples

Slime (default)
<SlimeButton>Launch</SlimeButton>
Slime — orbit
<SlimeButton variant="orbit">Save</SlimeButton>
Elastic — outlined
<ElasticButton variant="outlined">Subscribe</ElasticButton>
Jelly — viscous
<JellyButton variant="viscous">Drag me</JellyButton>
Impact — drop
<ImpactButton variant="drop">Delete</ImpactButton>
Brand colorsAny button accepts buttonColor/clickColor overrides.
<JellyButton buttonColor="#7EACB5" clickColor="#ffffff">Get started</JellyButton>
Requires Tailwind CSS — the buttons style themselves with utility classes. Without it they render unstyled.
Client components ('use client'). They use refs/useId; render on the client. No dynamic(ssr:false) is strictly required (no WebGL), but they won't animate during SSR.
These buttons do not currently auto-respect prefers-reduced-motion (registry supportsReducedMotion: false). If you need it, gate the animated variants yourself.
Accessibility: each button renders a native

Source

export { default as SlimeButton } from "./SlimeButton";
export type { SlimeButtonProps } from "./SlimeButton";

export { default as ElasticButton } from "./ElasticButton";
export type { ElasticButtonProps } from "./ElasticButton";

export { default as ImpactButton } from "./ImpactButton";
export type { ImpactButtonProps } from "./ImpactButton";

export { default as JellyButton } from "./JellyButton";
export type { JellyButtonProps } from "./JellyButton";
View on GitHub →Report an issue