@inglorious/motion
v2.0.0
Published
CSS-first motion helpers for Inglorious Web type composition.
Maintainers
Readme
@inglorious/motion
CSS-first motion primitives for @inglorious/web using type composition.
Install
pnpm add @inglorious/motion @inglorious/webAPI
withMotion(config)
Composes a type with a motion lifecycle driven by WAAPI.
- Adds
motionVariantChange(entity, variant)handler. - Adds
removeWithMotion(entity, payload, api)handler (plays exit variant, then removes entity). - Supports layout FLIP with
layout: true(or custom timing). - Supports shared layout transitions with
motionLayoutId. - Supports presence groups with
presence: { mode: "wait" }. - Wraps your render with a motion host and applies lifecycle classes:
${classPrefix}--start${classPrefix}--active${classPrefix}--end${classPrefix}--variant-<name>
Stability
- Stable:
- Variant lifecycle (
motionVariantChange,removeWithMotion) - Class phases (
--start,--active,--end,--variant-*) - Presence
mode: "wait"semantics (queue exits per group)
- Variant lifecycle (
- Experimental:
- Shared-layout behavior driven by
motionLayoutId - Internal FLIP heuristics and thresholds
- Shared-layout behavior driven by
Migration policy
- Patch versions: bug fixes and non-breaking behavior improvements.
- Minor versions: additive config and handlers; no required app changes.
- Major versions: breaking semantics or API shape changes.
- Deprecated behavior keeps a compatibility alias for at least one minor release when feasible.
Browser support and fallbacks
- Primary engine: WAAPI (
Element.animate). - If WAAPI is unavailable, motion still updates final styles and lifecycle classes (no smooth tween).
prefers-reduced-motion: reduceis honored by skipping tweening and committing end state.- For robust visual guarantees across browsers, use Playwright E2E (unit tests cover deterministic logic only).
Example
import { html } from "@inglorious/web"
import { withMotion } from "@inglorious/motion"
const card = {
render(entity, api) {
return html`
<article>
<button
@click=${() =>
api.notify(`#${entity.id}:motionVariantChange`, "hidden")}
>
Hide
</button>
<button
@click=${() => api.notify(`#${entity.id}:removeWithMotion`, "exit")}
>
Remove
</button>
</article>
`
},
}
const motionCard = [
card,
withMotion({
initial: "visible",
layout: true,
presence: { mode: "wait" },
variants: {
visible: {
frames: [
{ opacity: 0, transform: "translateY(12px)" },
{ opacity: 1, transform: "translateY(0)" },
],
options: { duration: 220, easing: "ease-out" },
},
hidden: {
frames: [{ opacity: 1 }, { opacity: 0.2 }],
options: { duration: 180, easing: "ease-in" },
},
exit: {
frames: [
{ opacity: 1, transform: "scale(1)" },
{ opacity: 0, transform: "scale(0.96)" },
],
options: { duration: 160, easing: "ease-in" },
},
},
}),
]