@shadow-js/motion
v1.0.4
Published
Tween and spring values, exit animations and animated list reordering, driven by signals
Readme
@shadow-js/motion
Signal-driven motion for ShadowJS 1.0.0: tween and spring values (useTween, useSpring), an element
animation over the Web Animations API with a fallback (animate), presence for content that leaves
(Presence and usePresence), FLIP for reordered keyed lists (captureLayout, playFlip), and the
reduced-motion preference everywhere. Each export is in its own module with sideEffects: false.
Install
npm install @shadow-js/motionQuick start
import { createRoot, flushSync, useSignal } from "@shadow-js/core";
import { easings, useTween } from "@shadow-js/motion";
import { expect, it } from "vitest";
it("eases a value to its target", () => {
const [target, setTarget] = useSignal(0);
const value = createRoot(() =>
useTween(target, { duration: 0, ease: easings.linear, reducedMotion: () => true }),
);
setTarget(100);
flushSync();
expect(value()).toBe(100);
});In a component, <Presence when={open()} exit={[{ opacity: 1 }, { opacity: 0 }]}>...</Presence> keeps its
children in a wrapper until the exit keyframes finish.
API
useTween(source, options)anduseSpring(source, options): accessors that move towardsource(). A retarget mid-flight starts from the value on screen (tween) or keeps the velocity (spring). Both stop requesting frames once they are done, and disposing their owner cancels the pending frame. Spring precision must be positive and finite; invalid precision snaps on the next frame. Damped springs also settle when floating-point rounding prevents further state changes, so precision below the representable resolution cannot keep an idle spring scheduling frames indefinitely.animate(element, keyframes, options): the Web Animations call, withfinished,cancelandfinish. Withoutelement.animatethe last keyframe is written to the style of anHTMLElement.Presence(component) andusePresence(options): content that leaves with an animation. Opening again during the exit cancels it.captureLayout(container)andplayFlip(container, before): FLIP for keyed children, returning a handle withcancel()andfinished; moving onlytransform. It does nothing with reduced motion.useReducedMotion(),prefersReducedMotionNow(),easings,frameClock.
Reduced motion comes from matchMedia("(prefers-reduced-motion: reduce)"). Where there is no
matchMedia (servers, some test environments) it reads as "not reduced". Effects never run on the
server, so the hooks only return their starting values there.
Links
- Recipes for a list that enters and exits, press feedback with a spring, a drawer, and canceling on abort.
- Reference:
/reference/motion/on the documentation site.
Dependencies
- Runtime: none.
- Peers:
@shadow-js/coreand@shadow-js/web^1.0.3: the reactive clock and the renderer the animation hooks drive.
Types
- Names from the root:
AnimateOptions,AnimationHandle,FlipOptions,MotionClock,Presence,usePresence,useSpring,useTween. - Type tests in
test-types/are compile-time checks;npm run typecheck:typesruns them.
Support
- Node.js
>=22.12.0. ESM only: useimport;requireis not supported. - Browsers: current evergreen releases.
- Versioning: every
@shadow-js/*package releases together at one version. Upgrade them as one set. - Documentation: ShadowJS documentation.
Using, changing and contributing
- Use the package (a person or a coding agent that builds with it): this README, and SKILL.md, the short page an agent reads first.
- Change the package (fix a bug, add a feature): AGENTS.md says how the repository is laid out, how to implement and test a change, and how a pull request is reviewed. It is written for people and coding agents alike.
- Take part: CONTRIBUTING.md is the short guide; SECURITY.md is for vulnerabilities.
