The enter animation problem has been with us since CSS transitions were invented. @starting-style finally gives us a clean, declarative solution.
There's a class of bug that every CSS developer encounters eventually, usually while trying to animate a toast notification or a dialog. You add the element to the DOM, apply the transition, and⦠nothing. The element snaps in instantly, ignoring the transition entirely.
The reason is simple once you understand it: CSS transitions animate between two computed states. When an element is first inserted, there is no previous state. The browser has nothing to interpolate from, so the transition is skipped.
The old workarounds
Most of us reached for the same duct-tape solutions. Force a reflow, then set the target state:
// Option 1: setTimeout (fragile, tied to frame timing)
toast.classList.remove('visible');
setTimeout(() => toast.classList.add('visible'), 16);
// Option 2: requestAnimationFrame (better, still manual)
requestAnimationFrame(() => {
requestAnimationFrame(() => toast.classList.add('visible'));
});
// Option 3: force reflow by reading layout property
toast.getBoundingClientRect();
toast.classList.add('visible');
All of these work, in the sense that they produce the animation. None of them feel right. They're coupling timing to the rendering pipeline in ways that are hard to test, hard to reason about, and easy to break with a browser update.
Enter @starting-style
The @starting-style rule, now in Baseline 2024, lets you declare what an element's computed style should be treated as on its very first render. It gives the browser a "from" state to animate from, without any JavaScript.
/* The element's normal state */
.toast {
opacity: 1;
transform: translateY(0);
transition: opacity 0.3s ease, transform 0.3s ease;
}
/* How the browser treats this element on its first render */
@starting-style {
.toast {
opacity: 0;
transform: translateY(-0.75rem);
}
}
That's it. Add the element to the DOM, give it the class .toast, and the browser automatically transitions from the @starting-style declarations to the element's computed style. No JavaScript timing tricks required.
Keep Reading about @starting-style on: peterbenoit.com
Top comments (0)