Skip to content

Motion — Imperative Player

For component-driven lifecycles (open/close a modal, enter/exit a toast) where you control timing in code rather than scroll/hover/tap, use createMotionPlayer instead of the and-motion attribute.

import { createMotionPlayer } from '@andersseen/motion';
const player = createMotionPlayer(element);
await player.play('fade-zoom-in');
// …later…
await player.play('fade-zoom-out');
player.destroy();

play(name) resolves on animationend. With respectReducedMotion: true (the default), it resolves immediately without running the animation when the user prefers reduced motion — callers don’t need a separate branch for that case. This is the mechanism @andersseen/web-components and @andersseen/vanilla-components use internally for the animated prop; use it directly if you’re building your own component layer.

createMotionPlayer(element, {
respectReducedMotion: true, // default
fillMode: 'both', // default
});
OptionTypeDefaultNotes
respectReducedMotionbooleantrueWhen the user prefers reduced motion, play() resolves immediately and skips the animation entirely — no separate code path needed.
fillMode'none' | 'forwards' | 'backwards' | 'both''both'Forwarded to animation-fill-mode. 'both' keeps the end-state applied after animationend and the start-state during any delay.
MethodSignatureNotes
play(name)(name: string) => Promise<void>Starts name, resolving on animationend. Calling play() again before that cancels the pending promise’s wait and starts fresh — safe to call repeatedly (e.g. on rapid open/close toggles).
stop()() => voidCancels the current animation and clears all and-motion* attributes/inline styles, without destroying the player — you can call play() again afterward.
destroy()() => voidSame cleanup as stop(); call this when the element itself is being removed and you won’t call play() again.

play(name) isn’t limited to the declarative catalog — it sets animation-name: and-${name} directly, so it can trigger any @keyframes and-* defined in core.css, including a smaller set of component-oriented tokens that have no [and-motion="..."] selector and therefore can’t be triggered by the attribute at all:

NameUsed by (internally)
fade-zoom-in / fade-zoom-outModal open/close
slide-in-from-right / slide-out-to-rightDrawer, Toast
slide-in-from-left / slide-out-to-leftavailable, unused by a built-in component
slide-in-from-top / slide-out-to-topavailable, unused by a built-in component
slide-in-from-bottom / slide-out-to-bottomavailable, unused by a built-in component
accordion-open / accordion-closeAccordion content panel
spinButton loading spinner
rotate-180 / rotate-180-reverseAccordion trigger chevron

fade-in/fade-out (from the main catalog) are also the entrance/exit used by Dropdown, Tooltip, and Alert — the full entrance/exit map @andersseen/web-components wires per component:

ComponentEntranceExit
Modalfade-zoom-infade-zoom-out
Drawerslide-in-from-rightslide-out-to-right
Toastslide-in-from-rightslide-out-to-right
Dropdownfade-infade-out
Tooltipfade-infade-out
Accordionfade-infade-out
Alertfade-infade-out

This map lives in @andersseen/web-components’s internal utils/animation.ts (not part of the public API — the table above is the useful part). If you’re building a custom component that should feel consistent with the rest of the library, reuse the same names via your own createMotionPlayer call rather than inventing new keyframes:

import { createMotionPlayer } from '@andersseen/motion';
const player = createMotionPlayer(myPanelElement);
async function open() {
await player.play('slide-in-from-bottom');
}
async function close() {
await player.play('slide-out-to-bottom');
}
Play "bounce-in"
Target element
<button id="trigger">Play "bounce-in"</button>
<div id="target">Target element</div>
<script type="module">
import { createMotionPlayer } from '@andersseen/motion';
const player = createMotionPlayer(document.getElementById('target'));
document.getElementById('trigger').addEventListener('click', () => {
player.play('bounce-in');
});
</script>