warm-motion
v0.1.0
Published
Tasteful, reduced-motion-aware React interaction primitives — cursor spotlight, magnetic hover, scroll reveals, view transitions.
Downloads
126
Maintainers
Readme
warm-motion
Tasteful, reduced-motion-aware React interaction primitives — a cursor spotlight, magnetic hover, scroll reveals, and view-transition helpers. Extracted from marekzska.space — the site runs on it.
Pre-release: packaged and publish-ready, but not yet on the npm registry. The API below is stable.
- Accessible by default — every effect honours
prefers-reduced-motionand bails on coarse (touch) pointers, so you never ship a dead hover affordance. - Owns its physics — the magnetic pull is clamped and spring-smoothed in JS (no reliance on a CSS transition), and its rAF loop idles when settled.
- Tiny & tree-shakeable — ~1.5 kB gzipped, ESM + CJS,
react/react-dom/framer-motionare peer deps.
Install
npm i warm-motion// once, near your app root:
import 'warm-motion/styles.css'Peer dependencies: react >=18, react-dom >=18, framer-motion >=11.
API
useSpotlight(options?)
Tracks the cursor into the --mx / --my CSS variables on :root and toggles a
spotlight-live class. Combined with the stylesheet, it lights up any element
with the .lit class.
useSpotlight() // options: { lerp?, staticX?, staticY? }
<h1 className="lit">Lit by the cursor</h1>Map your text colours onto the spotlight:
:root {
--wm-spotlight-from: #ede6e0; /* bright */
--wm-spotlight-to: #8a7e7e; /* dim */
}useMagnetic(options?) → ref
Attracts an element toward the cursor once it enters an activation radius. The
pull is clamped (maxOffset) and both follow and snap-back are spring-smoothed.
const ref = useMagnetic<HTMLAnchorElement>({ strength: 0.35, radius: 140, maxOffset: 12 })
<a ref={ref} href="…">Contact</a><Reveal>
Fades (and optionally rises) children into view once, when scrolled near. Renders a plain wrapper with no animation under reduced motion.
<Reveal delay={0.08} rise>
<p>…</p>
</Reveal>withViewTransition(update)
Runs update inside a View Transition when supported, synchronously otherwise,
and skips the transition entirely under reduced motion.
withViewTransition(() => flushSync(() => setTheme('dark')))Constants
EASE, REVEAL_DURATION, REVEAL_RISE, REVEAL_STAGGER — the shared timing the
primitives use, exported so your own motion can match.
Behaviour matrix
| Condition | useSpotlight | useMagnetic | Reveal | | --- | --- | --- | --- | | reduced motion | static fallback vars | inert | no animation | | coarse pointer | static fallback vars | inert | animates on scroll |
Versioning
Semantic versioning. The public surface is:
- the named JS exports above;
- the CSS custom properties
--wm-spotlight-from,--wm-spotlight-to, and--spotlight-radius(spotlight size, default30rem); - the
--mx/--mycursor variables written byuseSpotlight(read them for your own cursor-driven effects); - the
.litand.spotlight-liveclass names.
These names are intentionally generic — if they collide with your app, scope the stylesheet or override them.
License
MIT
