Understanding animation-composition: Additive Animations Without Overwriting
AI generated
{ }
@
CSS · animation-composition · Additive Animation
Understanding animation-composition
Additive Animations Without Overwriting Each Other

Two animations that both use transform overwrite each other by default. animation-composition with the values add and accumulate solves exactly this problem and makes it possible to run a hover effect, a scroll animation and an idle motion at the same time on the same property, without one animation swallowing the other.

15 min read replace · add · accumulate · Web Animations API Chrome 112+ · Firefox 115+ · Safari 16+

1. What animation-composition actually solves

As soon as two separate CSS animations target the same property, for example transform for a hover effect and transform for a parallel running idle floating animation, by default only one of the two wins. The browser replaces the value of one animation entirely with the value of the other, instead of combining them. This behavior is called replace and is the default value of animation-composition, which is exactly why many combined hover and idle effects feel jerky in practice or visibly break as soon as the user hovers the element while the idle animation is running.

Before animation-composition, this problem had to be worked around manually, usually with nested wrapper elements where each animation gets its own element so the transform values don't get in each other's way. That solution works, but it creates extra, semantically meaningless DOM nodes purely for animation purposes, which is neither elegant nor particularly maintainable.

The property animation-composition solves this problem at its root: instead of one animation overwriting the other, multiple animations on the same property can be summed additively. That matches the intuition most developers already have about animations much better, namely that two independent motions should add up, instead of one making the other invisible.

2. The three values: replace, add and accumulate

animation-composition knows three values, each defining a different interplay of multiple animations. replace is the default described above: the value of the later declared or higher priority animation entirely replaces the previous one. add appends the transformation of an animation to the one already present, mathematically as a matrix multiplication of the transform values, which works especially well for transform properties like translate, rotate or scale.

The third value, accumulate, behaves similarly to add, but differs in how it treats repetitions: with multiple runs of the same animation, the final values sum up instead of jumping back to zero on every iteration. For most practical cases of additively combining different animations, add is the more natural choice, accumulate is especially suited for animations that should keep accumulating continuously, for example a rotation that keeps growing across multiple interactions.


/* Default behavior: the second animation silently overrides the first */
.card {
  animation: idle-float 3s ease-in-out infinite;
}
.card:hover {
  animation: hover-lift 0.3s ease-out both;
  /* animation-composition: replace; (implicit default) */
}

@keyframes idle-float {
  50% { transform: translateY(-6px); }
}
@keyframes hover-lift {
  to { transform: translateY(-12px) scale(1.03); }
}

In the example above, the gentle idle floating motion disappears immediately once the hover state activates, because replace applies as the default behavior. This is exactly what changes once animation-composition: add; is set explicitly.

3. Additive animations for independent transform axes

The most convincing use case for animation-composition: add; is independent motion axes that are conceptually unrelated to each other but happen to use the same CSS property. A gentle idle floating motion on the Y axis and a hover triggered lift on the same Y axis are conceptually two different motions, but technically both part of the same transform property.

With animation-composition: add; on the second animation, both transformations get summed instead of the second replacing the first. The element keeps floating gently while the hover effect gets added on top at the same time, a behavior that without animation-composition would only be reachable with nested wrapper elements.


/* Both animations coexist: idle float stays active, hover adds on top */
.card {
  animation: idle-float 3s ease-in-out infinite;
}
.card:hover {
  animation: hover-lift 0.3s ease-out both;
  animation-composition: add;
}

@keyframes idle-float {
  50% { transform: translateY(-6px); }
}
@keyframes hover-lift {
  to { transform: translateY(-12px) scale(1.03); }
}

The order of the transform functions matters here: add combines the matrices in the order the animations sit in the composite stack, which can lead to unexpected results with more complex combinations of rotation and translation if the order was not chosen deliberately. A quick visual check in DevTools usually clarifies fast whether the added motion looks as expected.

4. Combining multiple animations on the same property

Beyond two animations, animation-composition can also be used for three or more animations running at once on the same property, as long as each additional animation is meant to contribute its own independent share to the overall transformation. A typical pattern: a continuous base motion, a state-dependent modification, and a brief reaction to a user interaction, all three at once on transform.

The ability to combine multiple additive animations makes complex, choreographed motion sequences possible that previously required either a single, monolithic @keyframes rule with all combined states, or several nested DOM elements. Both alternatives are considerably harder to maintain than three independently declared, additively composed animations.


/* Three independent animations, all additive on the same property */
.orb {
  animation:
    drift 6s ease-in-out infinite,
    pulse-scale 2s ease-in-out infinite;
  animation-composition: add, add;
}
.orb.is-active {
  animation: active-boost 0.4s ease-out both;
  animation-composition: add;
}

@keyframes drift {
  50% { transform: translateX(20px); }
}
@keyframes pulse-scale {
  50% { transform: scale(1.08); }
}
@keyframes active-boost {
  to { transform: translateY(-10px); }
}

5. accumulate in detail: final values instead of zero point

The difference between add and accumulate shows most clearly with repeatedly run animations. With add, each repetition cycle starts additively again at whatever value the other active animations provide at that moment, regardless of how often the cycle has already run. With accumulate, in contrast, the final values sum across multiple repetitions, so a rotation from 0 to 90 degrees effectively lands at 270 degrees after three repetitions, instead of ending at 90 degrees again every cycle.

This behavior is excellently suited for effects that should build up continuously across multiple interactions, for example an icon that turns a bit further on every click instead of starting over from scratch each time. accumulate is therefore the right choice for cumulative states, while add is meant for combining conceptually independent but simultaneously running motions.


/* accumulate: each click adds another full rotation cycle */
.spinner-icon {
  animation: none;
}
.spinner-icon.clicked {
  animation: quarter-turn 0.3s ease-out;
  animation-composition: accumulate;
}

@keyframes quarter-turn {
  to { transform: rotate(90deg); }
}

6. Practical example: hover and scroll animation at once

A particularly relevant real-world scenario combines a scroll-coupled animation with a hover interaction on the same element, for example a product card that gently slides into view while scrolling and should also react to hover at the same time. Without animation-composition, the hover animation would fully overwrite the scroll position transformation, causing a visible jump as soon as the user hovers the card while scrolling.

With animation-composition: add; on the hover animation, the scroll transformation stays fully intact while the hover effect applies additively on top. The result is a transition with no visible jump, because both motions, the scroll driven and the interaction driven one, are actually computed simultaneously and independently of each other.


.product-card {
  view-timeline: --card-view block;
  animation: card-reveal linear both;
  animation-timeline: --card-view;
  animation-range: entry 0% cover 40%;
}
.product-card:hover {
  animation: card-hover-lift 0.25s ease-out both;
  animation-composition: add;
}

@keyframes card-reveal {
  from { transform: translateY(40px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
@keyframes card-hover-lift {
  to { transform: translateY(-8px) scale(1.02); }
}

7. Interaction with the Web Animations API and composite

Anyone who drives animations dynamically via JavaScript with the Web Animations API already knows the composite option in element.animate(), which offers exactly the same semantics as the CSS property animation-composition: replace, add and accumulate are available there too. That is no coincidence, it is the same underlying specification, just with two different entry points, one declarative via CSS, one programmatic via JavaScript.

For projects that mix both declarative CSS animations and animations dynamically created with the Web Animations API, it is important to use the same composite strategy consistently on both layers. A CSS animation with animation-composition: add; combines correctly with a JavaScript animation that sets composite: 'add', as long as both target the same property and the browser supports the combination.


// Web Animations API: same composite semantics as animation-composition
element.animate(
  [{ transform: 'translateY(0)' }, { transform: 'translateY(-12px)' }],
  { duration: 300, easing: 'ease-out', composite: 'add', fill: 'both' }
);

8. Browser support and fallback without animation-composition

animation-composition is supported by most current evergreen browsers, but compared to basic CSS animation properties it is younger and therefore unavailable in older browser versions. Without support, the default value replace applies automatically, which in practice means the later animation overwrites the earlier one, exactly the behavior add was meant to avoid in the first place.

For projects that need broad browser support, a deliberate fallback test is recommended: does the combination of idle and hover animation still work acceptably without animation-composition, even if the additive effect is missing? In many cases a simple jump between states is tolerable, as long as the core functionality of the page is not affected. For more critical cases, the classic wrapper element solution remains a more robust, if considerably more elaborate, fallback.

9. animation-composition compared to manual transform merging

Deciding whether animation-composition or a classic, manual solution with nested elements should be used benefits from a direct comparison of the key properties.

Approach Extra DOM nodes Maintainability Browser support
animation-composition: add None High Modern evergreen browsers
Nested wrapper elements Yes, per animation Low Universal
One combined @keyframes rule None Low, inflexible Universal
JavaScript composite: 'add' None Medium Similar to CSS variant

animation-composition wins this comparison for any project that can prioritize modern browsers: no extra DOM nodes, high maintainability because each animation stays declared independently and readably. Only when very old browsers absolutely must be supported does the classic wrapper solution remain the more robust, if considerably more elaborate, alternative.

Mironsoft

Complex UI choreography without DOM bloat

Animations overwriting each other instead of adding up?

We analyze existing animation conflicts in your frontend and replace nested wrapper solutions with clean, additive animation-composition setups.

Analysis

Identifying conflicting transform animations in the existing codebase

Implementation

Cleanly implementing additive composition with add and accumulate

Fallback

Ensuring tested degradation behavior for older browsers

10. Summary

animation-composition solves the long-standing problem of competing animations on the same CSS property. The default value replace lets later animations fully overwrite earlier ones, add combines transformations additively, ideal for conceptually independent motions like idle floating and hover effects, and accumulate sums final values across multiple repetitions, ideal for cumulative states like continuously growing rotations.

The Web Animations API offers the same semantics programmatically through the composite option, keeping mixed CSS and JavaScript animation setups consistent. Without browser support, animation-composition automatically falls back to replace, which is why a deliberate test of the degradation behavior remains worthwhile for projects with broad browser coverage. Compared to the classic solution with nested wrapper elements, animation-composition saves DOM complexity and considerably improves maintainability.

Understanding animation-composition — Key Takeaways

replace (default)

The later animation fully overwrites the earlier one, no combination.

add

Combines transformations additively, ideal for independent motion axes like idle and hover.

accumulate

Sums final values across multiple repetitions, ideal for cumulative states.

Web Animations API

The composite option offers the same semantics programmatically, consistent with CSS.

11. FAQ: Understanding animation-composition

1What does animation-composition do?
Controls how multiple simultaneous animations on the same property combine: overwrite, add, or accumulate.
2add vs. accumulate?
add combines additively per frame, accumulate additionally sums final values across repetitions.
3Idle animation disappears on hover?
Default replace overwrites it. animation-composition: add fixes the problem.
4Best properties to use it for?
Transform based properties like translate, rotate and scale, due to clean matrix addition.
5Combining more than two animations?
Yes, any number can be summed additively if each gets its own composition value.
6Relation to composite in the Web Animations API?
Same specification, same values, just set programmatically instead of declaratively.
7Without browser support?
Automatic fallback to replace, no error, but no additive combination either.
8Does it fully replace nested wrappers?
In modern browsers yes, for very old browsers the wrapper solution remains as fallback.
9Does order affect the result?
Yes, add combines matrices in the order of the composite stack, which can change results.
10When to use accumulate instead of add?
When an effect should build up continuously across interactions, like a rotation that keeps turning further.