CSS animation-composition Property
Defines how multiple animations or an animation and its base styles should be combined when they affect the same property simultaneously.
| replace | The animation value completely overrides the underlying base value of the property. |
| add | The animation value is added to the underlying base value, similar to an append operation. |
| accumulate | The animation and underlying values are combined mathematically, allowing them to stack or grow together. |
Code Examples
This basic example shows how the add value allows the animation to start spinning from the base 45 degree rotation instead of resetting to 0.
<style>
.box {
width: 100px;
height: 100px;
background-color: #ff8800;
margin: 50px;
transform: rotate(45deg);
animation: spin 3s infinite linear;
animation-composition: add;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
</style>
<div class="box"></div>This advanced example uses JavaScript to toggle between composition modes, showing how the blur filter stacks with the base style or overrides it.
<style>
.orb {
width: 80px;
height: 80px;
background: #0077ff;
border-radius: 50%;
filter: blur(2px);
animation: pulse 2s infinite alternate;
}
</style>
<div class="orb" id="targetOrb"></div>
<button onclick="setComp('accumulate')">Accumulate</button>
<button onclick="setComp('replace')">Replace</button>
<script>
function setComp(mode) {
const orb = document.getElementById("targetOrb");
orb.style.animationComposition = mode;
}
const anim = document.getElementById("targetOrb").animate([
{ filter: "blur(10px)" }
], {
duration: 2000,
iterations: Infinity,
direction: "alternate"
});
</script>Pro Tip
If you are working with complex SVG animations or character rigs, use "accumulate" to layer minor secondary motions (like a slight breathing pulse) on top of larger primary motions (like a walking cycle) without writing massive, redundant keyframe blocks.
Deep Dive
Imagine painting a wall. The default behavior, "replace", is like painting a new solid color over an old one; the old color is hidden. Using "add" is like placing a transparent sticker over the existing paint so both are visible. "Accumulate" is like stacking physical bricks. If an element already has a "transform: scale(2)" and your animation applies "scale(1.5)", "replace" forces it to 1.5, whereas "accumulate" would mathematically combine them into a scale of 3. This property solves the headache of animations fighting each other for control. It allows you to build modular animations that stack on top of base CSS rules rather than wiping them out.
Best Practices
Use "replace" for simple, standalone animations where you want absolute control over the final state. Switch to "add" or "accumulate" when building complex UI components where different states (like a hover effect and a loading pulse) need to run at the same time without breaking the layout or resetting the base transforms.
Common Pitfalls
The biggest mistake is assuming animations stack by default. They do not; they replace. If you have a base transform on an element and your animation also uses transforms, the base one will vanish during the animation unless you set the composition to "add" or "accumulate". Also, remember that "add" and "accumulate" can produce different results depending on whether the property is a list (like filters) or a single value.
Accessibility
When stacking animations, be careful not to create cumulative motion that is too fast or erratic, as this can trigger seizures or nausea in users with vestibular disorders. Always respect the prefers-reduced-motion media query.
Dev Data Table: animation-composition property
| default | replace |
| animatable | no |
| inherited | no |
| experimental | no |
| year_intro | 2021 |
| year_standard | 2023 |
| js_syntax_1 | element.style.animationComposition = "accumulate"; |
| js_syntax_2 | element.style.setProperty("animation-composition", "add"); |
| js_note | This property is particularly useful when using the Web Animations API to layer multiple effects dynamically. |
| browsers | { "Chrome": 111, "Edge": 111, "Firefox": 115, "Safari": 16, "Opera": 97, "Chrome Android": 111, "Safari on iOS": 16, "Samsung Internet": 22, "Opera Mobile": 75 } |