Ever wondered why we spend so much time fighting the cascade when it comes to motion? For years, the biggest headache in CSS animation has been the 'winner takes all' mentality. If you have a button with a hover lift and you try to add a pulse effect simultaneously, the second one usually nukes the first. It's frustrating, and it's led many of us down a dark path of JavaScript-heavy motion libraries just to handle simple layering.
Right, let's talk about animation-composition. This property, part of the CSS Animations Level 2 spec, is a total game-changer for anyone building design systems. It changes the fundamental rule of how multiple animations affecting the same property are combined. Instead of just replacing the previous value, we can now add or accumulate them.
The Problem with the 'Replace' Default
By default, CSS uses replace. If Animation A moves an element 10px right and Animation B (applied later in the list) moves it 20px right, the element ends up at 20px. Animation A is effectively dead in the water. To fix this, we've historically had to create 'wrapper' divs for every single layer of motion or write massive, redundant keyframe blocks that try to do everything at once. It's a maintenance nightmare.
The Three Composition Modes
The new property gives us three distinct ways to handle these collisions. I've found that understanding the nuance between add and accumulate is where the real power lies:
- replace: The default. The last animation in the list wins and overrides everything else.
- add: The values are added together. If you have two transforms, they are concatenated. It's like saying 'do this AND do that'.
- accumulate: This is more like 'merging'. For things like scale or blur, it computes a combined value. For transforms, it follows a specific order (base then effect).
Building Reusable Motion Primitives
In a design system, we want primitives. We want a lift animation and a wobble animation that can live independently. With animation-composition: add, we can finally do that. Check out how clean this component API becomes:
/* Our motion primitives */
@keyframes lift {
to { transform: translateY(-10px); }
}
@keyframes shake {
to { transform: translateX(5px); }
}
.card-interactive {
/* We can layer these without them fighting! */
animation-name: lift, shake;
animation-duration: 0.3s, 0.1s;
animation-composition: add, add;
}
This approach is incredibly scalable. You can define your 'base' motion for a component and then layer 'state' motion (like :active or :focus) on top without rewriting the entire transform stack. It's the kind of architectural clean-up that makes my UX engineering heart happy.
Common Gotchas to Watch For
I've been playing with this in production-like environments, and there are a few things that might trip you up. First, remember that animation-composition itself isn't animatable; it's a configuration property. Second, the order of your values in the animation-composition list must match your animation-name list. If they get out of sync, you'll see some very weird visual bugs.
Also, don't assume this is only for multiple animations. Even with a single animation, add can be useful if you want to animate a property relative to its current state rather than overriding it entirely. It's a subtle shift in mindset but a powerful one.
Browser Support is Ready
The best part? Support is actually great. Chrome and Edge (112+), Safari (16+), and Firefox (115+) all support it. We're finally at a point where we can stop using 'hacks' and start using the platform as it was intended for complex motion orchestration.
Wrapping Up
- Use
animation-composition: addto layer multiple transform-based animations on a single element. - Stop duplicating keyframes for state changes; use motion primitives instead.
- Keep your animation lists in sync to avoid unexpected composite results.
I highly encourage you to go into your current design system and see where you're using wrapper divs just for animation. Try replacing them with a single element and animation-composition. It's a great exercise in simplifying your DOM.
If you want to go deeper and learn how to build real, production-ready CSS design systems step by step, check out my full course here: CSS Design Systems Course
If this is the kind of thing you keep finding out late about, my book Modern CSS 2026 on Amazon is one chapter per shift, with honest support labels.
Let's keep the conversation going! Catch me on Twitter for daily CSS tips or connect on LinkedIn to talk design system architecture.