○
Subtle refined
Barely-there premium nav baseline
Showcase: preset="punch" · strength 1.00
pnpm add @caustics/magnetic-elements <!-- Svelte 5 — declarative action --> <script> import { magnetic } from '@caustics/magnetic-elements/svelte'; </script> <button use:magnetic={{ preset: 'punch' }}>Get Started</button>
pnpm add @caustics/magnetic-elements /* React — hook returns a ref */ import { useMagnetic } from '@caustics/magnetic-elements/react'; const ref = useMagnetic({ preset: 'punch' }); return <button ref={ref}>Get Started</button>;
pnpm add @caustics/magnetic-elements <!-- Vanilla — zero JS per element via data attributes --> <script type="module"> import { init } from '@caustics/magnetic-elements/auto-init'; init(); </script> <button data-magnetic data-magnetic-preset="punch">Get Started</button>
Each preset is a tuned combination of physics mode and parameters — five physics
algorithms (lerp, spring, magnetic field, orbital, jitter) cross four character
sets (refined, default, punchy, editorial). Hover any card to feel the preset
react. Set one with preset="punch", override individual props as needed.
| Prop | Type | Default | Description |
|---|---|---|---|
| preset | PresetName | — | One of 11 curated configurations. User props override preset values. |
| physics | PhysicsMode | 'lerp' | 'lerp' · 'spring' · 'magnetic' · 'orbital' · 'jitter' |
| radius | number | 120 | Pixels at which attraction begins (uses fieldRadius for magnetic mode). |
| maxDisplacement | number | 16 | Hard pixel ceiling on element movement after all physics. |
| origin | 'center' | 'cursor-relative' | 'center' | Direction-only attraction vs. 1:1 cursor follow within radius. |
| strength | number | 1 | Output multiplier applied after physics. Range 0–2. |
| lockX / lockY | boolean | false | Constrain motion to a single axis (vertical-only or horizontal-only). |
| group | string | GroupConfig | — | Coordinate motion across grouped elements (independent / repulsion / coordinated). |
| returnDuration | number | 0 | When >0, deterministic JS tween on cursor-leave (ms). 0 = natural physics decay. |
| reducedMotion | 'pause' | 'subtle' | 'ignore' | 'pause' | Behaviour under prefers-reduced-motion: reduce. |
| onEnter / onLeave / onUpdate | callback | — | Lifecycle hooks. Plus a global onMagneticActivity event bus for cross-instrument coordination. |
caustics-magnetic-elements/ ├── svelte/ MagneticElements.svelte · index.ts · types.ts ├── react/ MagneticElements.tsx · index.ts · types.ts ├── vanilla/ magnetic-elements.ts · compiled .js ├── docs/ README.md · API.md · CHANGELOG.md ├── LICENSE.md Caustics Commercial License └── package.json
Magnetic Elements brings physical weight to hover. Buttons lean toward the cursor with springy commitment. Navigation links yield to the active link with quadratic falloff. Card grids tilt sympathetically as the cursor sweeps across them. The whole motion vocabulary you’ve felt on Apple, Linear, and Stripe — as a single 8 KB module.
Five physics modes (lerp, spring, magnetic, orbital, jitter) cover everything from baseline nav-link feel to ambient drifting decoration. Eleven curated presets are tuned for the use cases that matter — primary CTAs, secondary nav, content cards, decorative accents — and per-axis locks let you constrain motion to a single dimension for navigation rails or vertical hover lifts.
A single shared requestAnimationFrame loop services every magnetic element on the page, frame-rate-independent on 60 Hz and 120 Hz displays alike. Read/write batching prevents layout thrash. Off-screen elements are skipped via IntersectionObserver. The loop pauses entirely when the tab is hidden. Reduced motion is respected by default.
Activate declaratively with data-magnetic attributes — zero JavaScript required for the typical case. Or call magnetize(el, config) for fine-grained control. Svelte 5 actions and React 18+ hooks ship in the box.
The CSS custom-property bridge writes --magnetic-distance (0 → 1 with cursor proximity) and --magnetic-active to every magnetic element each frame. Drive arbitrary CSS — color, blur, shadow intensity, opacity, gradient stops — from cursor distance, with no JavaScript event listeners required. Designers stay in their toolchain; engineers stay in theirs.
Group coordination is the showpiece. Repulsion mode pushes inactive neighbours away from the active element — perfect for primary navigation where the focused link should feel emphasized. Coordinated mode applies sympathetic displacement so a card grid leans together as the cursor sweeps across it — the surface feels like one responsive plane.
A global event stream (onMagneticActivity) lets other components subscribe to magnetic activity — drive a custom cursor, trigger a sound, or coordinate with the rest of your interaction layer. A deterministic return-to-rest tween (returnDuration / returnEasing) is available for designers who want a specific decay curve instead of natural physics decay. The contract is the foundation for cross-instrument coordination as the Caustics catalog grows.
Caustics Commercial License · lifetime updates within v1