animation-composition: accumulate vs add
Part of CSS Animation Composition & Layering in Core CSS Animation Fundamentals.
The problem
Two effects touch the same transform: an entrance that scales a card from scale(0.9) to scale(1), and a hover that should enlarge whatever the card currently is by a further 10%. The default composition, replace, throws away one of them. Switching to add keeps both — and the card ends up at scale(1.1) on hover, which looks right. Then someone changes the hover to scale(0.1)-style offsets expressed as deltas, or layers two rotations with different origins, and the results stop matching intuition.
add and accumulate both “combine” values, and for many properties they produce the same number. For transform lists, filter lists and a few others they do not, and picking the wrong one produces motion that is subtly — or dramatically — off.
Root cause analysis: appending versus summing
add appends. For list-valued properties such as transform and filter, add produces the underlying list followed by the effect’s list. translateX(40px) added to translateX(10px) becomes translateX(10px) translateX(40px). Transform functions compose by matrix multiplication, so appended functions are applied one after another, each in the coordinate system the previous one left behind.
accumulate sums. For matching functions, accumulate combines the arguments into one function. translateX(40px) accumulated onto translateX(10px) becomes translateX(50px). For translations the visible result is the same as add. The difference appears with functions whose composition is not the same as summing.
Scale is the clearest case. scale(2) added to scale(1.5) appends to scale(1.5) scale(2), which multiplies to an effective scale(3). Accumulating uses the rule for scale, which sums the amounts relative to identity: 1.5 + 2 − 1 = 2.5. Rotation around the same origin gives equal results either way, but a rotate appended after a translate rotates the translated coordinate system, while accumulating a rotate into a list with no matching function falls back to appending.
For numeric properties such as opacity or a registered <number> custom property, both modes simply sum the values — the distinction is about lists and functions.
Step-by-step resolution
Production code pattern
/* Entrance: grows the card into place. */
.card {
animation: enter 400ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes enter {
from { transform: scale(0.9); }
to { transform: scale(1); }
}
/* Hover pulse: adds 0.06 to whatever scale the card has, mid-entrance or not. */
.card:hover {
animation: enter 400ms cubic-bezier(0.2, 0.8, 0.2, 1) both,
pulse 600ms ease-in-out infinite alternate;
animation-composition: replace, accumulate; /* per-animation, in list order */
}
@keyframes pulse {
to { transform: scale(1.06); } /* accumulate: underlying + 0.06 */
}
/* A soft focus blur layered on a loading blur: one merged filter pass. */
.panel.is-loading { filter: blur(2px); }
.panel.is-loading.is-dimmed {
animation: deepen 300ms ease-out forwards;
animation-composition: accumulate; /* blur(2px) + blur(3px) = blur(5px) */
}
@keyframes deepen { to { filter: blur(3px); } }
@media (prefers-reduced-motion: reduce) {
.card, .card:hover { animation: none; }
.panel.is-loading.is-dimmed { animation: none; filter: blur(5px); }
}
Rendering Impact: composite for the scale effects; paint for the blur. Composition happens when the animated value is computed, so the mode does not change the rendering tier — but merging filters with
accumulateavoids two sequential blur passes, which halves the filter’s paint work.
The pulse keyframe uses scale(1.06), and with accumulate the effect contributes +0.06 on top of whatever the entrance currently produces. With add the same keyframe would multiply — scale(0.95) scale(1.06) mid-entrance — which is visually close for small factors but diverges quickly for larger ones.
Verification checklist
Constraints and trade-offs
accumulatefalls back to appending when the function lists do not match in type and order.- Mixing composition modes across many layered effects makes final values hard to reason about; document the intent next to the rule.
- Matrix interpolation may be used when lists cannot be matched, which can produce unexpected rotations mid-animation.
- Older engines ignore
animation-compositionand usereplace; the top effect wins there. - Individual transform properties (
scale,rotate,translate) often remove the need for composition entirely by giving each effect its own property.
Frequently asked questions
When do add and accumulate give different results?
For list-valued properties like transform and filter when the functions do not simply sum, such as scale, or when function lists differ. For plain numbers like opacity both modes sum the values.
Is accumulate just add for numbers?
Yes. For numeric properties both modes add the underlying and effect values. The distinction only matters for lists of functions.
Should I use individual transform properties instead?
Often. If the entrance animates translate and the hover animates scale, putting them on separate properties avoids composition questions entirely. Composition is for effects that must share one property.
Related
- CSS Animation Composition & Layering — the parent topic
- Layering Animations with animation-composition: add — the add mode in a real entrance-plus-hover case
- Individual Transform Properties vs Shorthand — separating effects instead of composing them