The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Replace the underlying value | .pulse { animation-composition: replace; } | View examples |
| Add an animation effect | .badge { animation-composition: add; } | View examples |
| Accumulate an effect | .counter { animation-composition: accumulate; } | View examples |
| Change composition at a keyframe | @keyframes move {
from { translate:
0; animation-composition: add; } to { translate: 2rem; };
} | View examples |
| Coordinate two animations | animation-name:
drift, pulse; animation-duration: 2s, 800ms; animation-composition: add, replace; | View examples |
| Add independent transforms | .icon {
animation:
spin 2s linear infinite, bob 1s ease-in-out infinite alternate;
animation-composition: add, add;
} | View examples |
| Separate transform components | .card { translate: 0 0; rotate: 0deg; scale: 1; } | View examples |
| Use standard ease | .panel { animation-timing-function: ease; } | View examples |
| Define a Bézier curve | .panel {
animation-timing-function: cubic-bezier(.2, .8, .2, 1);
} | View examples |
| Create discrete frames | .sprite {
animation-timing-function: steps(8, jump-end);
} | View examples |
| Jump immediately | .switch { animation-timing-function: step-start; } | View examples |
| Define sampled linear easing | .spring {
animation-timing-function:
linear(0, .7 40%, 1.05 70%, 1);
} | View examples |
| Ease one segment | @keyframes lift {
0% { translate:
0; animation-timing-function: ease-out; } 60% { translate: 0 -1rem; };
} | View examples |
| Avoid relying on final easing | @keyframes fade {
0% { opacity:
0; animation-timing-function: ease-out; } 100% { opacity: 1; };
} | View examples |
| Opt into discrete transitions | .popover {
transition: opacity .2s, display .2s allow-discrete;
} | View examples |
| Define an entry start style | @starting-style {
.popover:popover-open { opacity: 0; };
} | View examples |
| Respect reduced motion | @media (prefers-reduced-motion: reduce) {
.animated { animation: none; transition: none; };
} | View examples |
| Hint only imminent animation | .card.is-about-to-move { will-change: transform; } | View examples |
When multiple animations target the same property, composition decides whether the later effect replaces or combines with the underlying value. Easing maps timeline progress to effect progress and can be applied at the animation or keyframe-segment level. This guide covers composite order, transform composition, list coordination, modern linear() curves, discrete transitions, performance, and motion accessibility.
Step by step
Detailed examples
Choose replacement, addition, or accumulation deliberately
animation-composition is a coordinated list with replace as its initial value. replace discards the underlying value for that effect; add and accumulate use property-specific composite operations and are not meaningful for every property. For transforms, addition combines transform lists in order, so rotation then translation can differ from translation then rotation. A keyframe can set composition for subsequent segments of properties declared there.
.icon { rotate:10deg; animation:nudge 400ms ease-out; animation-composition:add; }
@keyframes nudge { from { translate:0; } 50% { translate:0 -8px; } to { translate:0; } } Reason about composite order and list positions
CSS animations on an element have defined composite order; later effects can cover earlier effects under replace. Each animation-* property is a comma-separated list coordinated by index, with shorter lists repeated or truncated according to CSS list rules. Prefer individual translate, rotate, and scale properties when they express independent concerns, or document transform composition order carefully. Transitions sit at a lower composite order than CSS animations.
.icon { animation-name:spin,bob; animation-duration:2s,1s; animation-iteration-count:infinite,infinite; animation-direction:normal,alternate; animation-composition:add,add; } Match easing grammar to the experience
ease keywords are cubic Bézier curves; cubic-bezier() requires x control coordinates in [0,1], while y may overshoot. steps() is correct for sprites or discrete state frames, with jump terms defining endpoint inclusion. linear() defines piecewise-linear output stops and may overshoot, but availability is newer. A timing function changes progress, not duration, and applies separately to each keyframe segment.
.drawer { animation-timing-function:cubic-bezier(.2,.8,.2,1); animation-timing-function:linear(0,.7 40%,1.05 70%,1); } Put segment easing on the starting keyframe
animation-timing-function declared in a keyframe controls the interval from that keyframe to the next; a declaration on the final keyframe does not affect an interval. Missing endpoint property values are synthesized from the underlying style when possible. Duplicate keyframe offsets cascade together rather than one entire block automatically replacing another. Keep keyframes focused so later maintenance does not create accidental composition.
@keyframes settle {
0% { translate:0 1rem; opacity:0; animation-timing-function:ease-out; }
70% { translate:0 -.15rem; opacity:1; animation-timing-function:ease-in; }
100% { translate:0; opacity:1; }
} Coordinate entry and exit without hiding focus prematurely
Discrete properties do not interpolate continuously. transition-behavior:allow-discrete and the allow-discrete shorthand component opt selected transitions into their defined flip timing; @starting-style supplies a before-change style for newly rendered elements. Support is evolving, so the baseline must show and hide correctly without animation. Do not set display:none or inert before an exit completes if that would strand focus; move focus and update accessibility state with application logic.
.tip:popover-open { opacity:1; transition:opacity .2s,display .2s allow-discrete,overlay .2s allow-discrete; }
@starting-style { .tip:popover-open { opacity:0; } } Prefer compositor-friendly, optional motion
Transforms and opacity commonly avoid layout, though large layers and filters still cost memory and paint. Animating dimensions or registered layout tokens can recalculate layout every frame. Use will-change sparingly and temporarily. Reduce or remove parallax, scaling, repeated pulsing, and large spatial movement under prefers-reduced-motion; a short opacity change may be acceptable, but user preference and product context should drive the choice. Never make animation the only state cue.
.notice { animation:arrive 500ms ease-out both; }
@media (prefers-reduced-motion:reduce) { .notice { animation:none; } }
.notice::before { content:"Ready"; } Local code tester
Compare composed motion and easing
Choose replace or add and a standard or sampled easing curve; reduced-motion preferences disable the movement.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
- CSS Working GroupCSS Animations Module Level 2drafts.csswg.org
- CSS Working GroupCSS Easing Functions Module Level 2drafts.csswg.org
- CSS Working GroupWeb Animations Level 2drafts.csswg.org
- CSS Working GroupCSS Transitions Level 2drafts.csswg.org
- Web Accessibility InitiativeUnderstanding Success Criterion 2.3.3: Animation from Interactionsw3.org
- MDN Web Docsanimation-compositiondeveloper.mozilla.org
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



