The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
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 animationsanimation-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

01

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.

Add a nudge to an existing rotation
.icon { rotate:10deg; animation:nudge 400ms ease-out; animation-composition:add; }
@keyframes nudge { from { translate:0; } 50% { translate:0 -8px; } to { translate:0; } }
Back to quick reference ↑
02

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.

Two named effects with explicit coordination
.icon { animation-name:spin,bob; animation-duration:2s,1s; animation-iteration-count:infinite,infinite; animation-direction:normal,alternate; animation-composition:add,add; }
Back to quick reference ↑
03

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.

Fallback curve before a sampled spring
.drawer { animation-timing-function:cubic-bezier(.2,.8,.2,1); animation-timing-function:linear(0,.7 40%,1.05 70%,1); }
Back to quick reference ↑
04

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.

Distinct rise and settle curves
@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; }
}
Back to quick reference ↑
05

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.

Progressive popover transition
.tip:popover-open { opacity:1; transition:opacity .2s,display .2s allow-discrete,overlay .2s allow-discrete; }
@starting-style { .tip:popover-open { opacity:0; } }
Back to quick reference ↑
06

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.

Reduced-motion end state
.notice { animation:arrive 500ms ease-out both; }
@media (prefers-reduced-motion:reduce) { .notice { animation:none; } }
.notice::before { content:"Ready"; }
Back to quick reference ↑

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.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. CSS Working GroupCSS Animations Module Level 2drafts.csswg.org
  2. CSS Working GroupCSS Easing Functions Module Level 2drafts.csswg.org
  3. CSS Working GroupWeb Animations Level 2drafts.csswg.org
  4. CSS Working GroupCSS Transitions Level 2drafts.csswg.org
  5. Web Accessibility InitiativeUnderstanding Success Criterion 2.3.3: Animation from Interactionsw3.org
  6. 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.

Share feedback