Skip to content
Carlo Fontanos
Carlo Fontanos

Carlo Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

element.animate(): Animations Without a Stylesheet Round-Trip

C
Carlo Fontanos
· 2 min read

CSS animations are perfect until the values come from data - an element flying from this clicked position to that cart icon. Suddenly you're injecting style tags or juggling classes and transitionend listeners. The Web Animations API is the missing middle: keyframe semantics, JavaScript values.

const anim = toast.animate(
    [
        { opacity: 0, transform: 'translateY(12px)' },
        { opacity: 1, transform: 'translateY(0)' },
    ],
    { duration: 250, easing: 'ease-out' }
);

await anim.finished;        // a real promise - no transitionend bookkeeping
toast.dataset.state = 'shown';

The finished promise alone justifies the API: sequencing animations becomes plain async code, with none of the "did the event fire before I subscribed" races that transitionend loves.

The fill: 'forwards' trap and its fix

Animations revert when they finish - your faded-in toast snaps invisible again. The reflex fix, fill: 'forwards', works but keeps the animation object alive forever holding those styles (they even beat !important, being composited on top). The clean pattern:

const anim = el.animate(keyframes, { duration: 300, fill: 'forwards' });
await anim.finished;
anim.commitStyles();   // write final values into el.style...
anim.cancel();         // ...and release the animation machinery

Where WAAPI beats CSS outright

// Data-driven keyframes - impossible in a stylesheet
card.animate(
    [
        { transform: `translate(${fromX}px, ${fromY}px) scale(1)` },
        { transform: 'translate(0, 0) scale(1)' },
    ],
    { duration: 350, easing: 'cubic-bezier(0.2, 0, 0.2, 1)' }
);

That's the FLIP technique in its natural habitat: measure old position, measure new, animate the delta - list reordering and shared-element effects that feel native. Live playback control is the other differentiator: anim.pause(), reverse(), updatePlaybackRate(2), seeking via currentTime - a menu that smoothly reverses mid-open when the user changes their mind is two method calls.

Sanity rules (same physics as CSS)

  • Stick to transform and opacity for 60fps - WAAPI animations of compositable properties run off the main thread, same as CSS.
  • Respect prefers-reduced-motion: check the media query and pass duration: 0.
  • Static, always-the-same animations still belong in CSS - visible to design tools, cacheable, declarative. WAAPI is for values born at runtime.

Support has been universal for years (the promise and commitStyles bits since ~2020-2021). For whole-page transitions between states, its sibling the View Transitions API picks up where per-element animation ends.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Your details are only used to reply to you.

Keep reading