CSS 57 💻 animation properties part 2
This lesson covers the remaining animation properties: animation-fill-mode, animation-play-state, animation-timeline, and animation-composition.
Overview of Properties
| Property | Description | Default |
|---|---|---|
animation-fill-mode | Styles before/after animation | none |
animation-play-state | Running or paused | running |
animation-timeline | Timeline that controls the animation | auto |
animation-composition | How multiple animations combine | replace |
1. animation-fill-mode
Defines the styles applied to an element before and after the animation plays.
.box {
animation-fill-mode: forwards;
}
Values
| Value | Description |
|---|---|
none | No styles applied outside the animation (default) |
forwards | Keep the last keyframe’s styles after the animation ends |
backwards | Apply the first keyframe’s styles during the delay |
both | Apply both forwards and backwards |
Visual Explanation
Without fill-mode:
[before] [animation] [after → reverts to original]
With forwards:
[before] [animation] [after → keeps end state]
With backwards:
[before → shows start state] [animation] [after]
With both:
[before → shows start state] [animation] [after → keeps end state]
Key Points:
forwards— the element stays at the animation’s end statebackwards— the element shows the start state during the delayboth— combines both effects- Without
fill-mode, the element snaps back to its original state after the animation
2. animation-play-state
Controls whether an animation is running or paused.
.box {
animation-play-state: paused;
}
Values
| Value | Description |
|---|---|
running | Animation is playing (default) |
paused | Animation is paused |
Key Points:
- Useful for pausing animations on hover
- The animation resumes from where it was paused
- Commonly used with
:hoverfor interactive control
Example:
.animation:hover {
animation-play-state: paused;
}
3. animation-timeline
Allows you to specify a timeline that controls the animation instead of the default document timeline.
.box {
animation-timeline: scroll();
}
Values
| Value | Description |
|---|---|
auto | Default document timeline (default) |
none | No timeline — animation doesn’t play |
scroll() | Binds animation to scroll position |
view() | Binds animation to element visibility in viewport |
scroll() Timeline
.box {
animation-timeline: scroll();
}
The animation progresses in sync with scrolling — as the user scrolls, the animation advances. This creates scroll-driven animations that feel connected to the user’s interaction.
Variations:
animation-timeline: scroll(); /* Root scroller */
animation-timeline: scroll(root); /* Root scroller */
animation-timeline: scroll(nearest); /* Nearest scrollable ancestor */
animation-timeline: scroll(self); /* The element itself */
view() Timeline
.box {
animation-timeline: view();
}
The animation progresses based on the element’s visibility within the viewport — as the element enters, crosses, and exits the viewport, the animation plays.
Key Points:
animation-timelineis a modern feature with growing browser support- Scroll-driven animations are performant — they run on the compositor thread
- Always provide a fallback for browsers that don’t support it
4. animation-composition
Controls how multiple animations that target the same properties combine their effects.
.box {
animation-composition: add;
}
Values
| Value | Description |
|---|---|
replace | New animations overwrite previous ones (default) |
add | New animations add to existing effects |
accumulate | Similar to add, but with additional rules for keyframes and timing |
How It Works
replace (default): The last animation wins.
.box {
animation-name: moveRight, fadeOut;
animation-composition: replace;
/* fadeOut's opacity replaces any opacity from moveRight */
}
add: Effects are combined.
.box {
animation-name: moveRight, moveUp;
animation-composition: add;
/* The box moves both right AND up */
}
accumulate: Similar to add, but keyframe values accumulate.
.box {
animation-name: scale1, scale2;
animation-composition: accumulate;
/* scale1 and scale2 combine their scale values */
}
Key Points:
replaceis the default — later animations override earlier onesaddcombines transform effects (e.g., translate + rotate)accumulateis useful for stacking animations with the same property
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animation Properties — Part 2</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 200px;
overflow: hidden;
}
/* ====== BASIC ANIMATION ====== */
.box {
width: 100px;
height: 100px;
background: #007bff;
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
animation-name: exampleAnimation;
animation-duration: 2s;
animation-fill-mode: forwards;
}
@keyframes exampleAnimation {
from {
transform: translateX(0);
background-color: #007bff;
}
to {
transform: translateX(200px);
background-color: #dc3545;
}
}
/* ====== FILL MODE COMPARISON ====== */
.fill-box {
width: 80px;
height: 80px;
background: #007bff;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.7rem;
animation-name: fillDemo;
animation-duration: 1s;
animation-delay: 0.5s;
animation-timing-function: ease;
}
.fill-none {
animation-fill-mode: none;
}
.fill-forwards {
animation-fill-mode: forwards;
}
.fill-backwards {
animation-fill-mode: backwards;
}
.fill-both {
animation-fill-mode: both;
}
@keyframes fillDemo {
from {
opacity: 0;
transform: translateX(-50px);
background: #dc3545;
}
to {
opacity: 1;
transform: translateX(0);
background: #28a745;
}
}
/* ====== PLAY STATE ====== */
.play-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.play-box {
width: 100px;
height: 100px;
background: #6c5ce7;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
animation: pulseAnim 1.5s ease-in-out infinite;
}
.play-box:hover {
animation-play-state: paused;
}
@keyframes pulseAnim {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(108, 92, 231, 0.7);
}
50% {
transform: scale(1.15);
box-shadow: 0 0 0 15px rgba(108, 92, 231, 0);
}
}
/* ====== COMPOSITION ====== */
.composition-box {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.6rem;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
.comp-replace {
background: #007bff;
animation-name: moveRight, rotateBox;
animation-composition: replace;
}
.comp-add {
background: #28a745;
animation-name: moveRight, moveUp;
animation-composition: add;
}
.comp-accumulate {
background: #ffc107;
color: #333;
animation-name: scaleUp, scaleDown;
animation-composition: accumulate;
}
@keyframes moveRight {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
@keyframes moveUp {
from { transform: translateY(0); }
to { transform: translateY(-50px); }
}
@keyframes rotateBox {
from { transform: rotate(0deg); }
to { transform: rotate(180deg); }
}
@keyframes scaleUp {
from { transform: scale(1); }
to { transform: scale(1.5); }
}
@keyframes scaleDown {
from { transform: scale(1); }
to { transform: scale(0.7); }
}
/* ====== SCROLL TIMELINE ====== */
.scroll-container {
height: 300px;
overflow-y: scroll;
background: #f8f9fa;
border: 2px solid #ddd;
border-radius: 8px;
margin: 15px 0;
position: relative;
}
.scroll-content {
height: 1200px;
padding: 20px;
}
.scroll-animated-box {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 12px;
position: sticky;
top: 20px;
animation-name: scrollAnimation;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
}
@keyframes scrollAnimation {
from {
transform: scale(0.5) rotate(0deg);
background: #007bff;
opacity: 0.5;
}
to {
transform: scale(1) rotate(360deg);
background: #28a745;
opacity: 1;
}
}
.scroll-instruction {
position: sticky;
top: 0;
background: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 8px;
text-align: center;
font-weight: bold;
color: #007bff;
z-index: 10;
}
/* ====== PRACTICAL: HOVER-PAUSE CARD ====== */
.hover-pause-container {
display: flex;
gap: 30px;
justify-content: center;
flex-wrap: wrap;
margin: 20px 0;
}
.hover-pause-card {
width: 180px;
height: 180px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
text-align: center;
padding: 20px;
cursor: pointer;
animation: floatCard 3s ease-in-out infinite;
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.2);
}
.hover-pause-card:hover {
animation-play-state: paused;
}
.hover-pause-card .icon {
font-size: 2.5rem;
margin-bottom: 10px;
}
.hover-pause-card h4 {
margin: 0;
font-size: 1rem;
}
.hover-pause-card p {
margin: 5px 0 0 0;
font-size: 0.8rem;
opacity: 0.9;
}
@keyframes floatCard {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
/* ====== PRACTICAL: SCROLL PROGRESS ====== */
.progress-scroll-container {
height: 250px;
overflow-y: scroll;
background: #f8f9fa;
border: 2px solid #ddd;
border-radius: 8px;
margin: 15px 0;
position: relative;
}
.progress-scroll-content {
height: 1000px;
padding: 20px;
}
.progress-indicator {
position: sticky;
top: 0;
height: 8px;
background: #e9ecef;
border-radius: 4px;
z-index: 10;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #007bff, #6c5ce7);
border-radius: 4px;
animation-name: progressGrow;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
transform-origin: left;
}
@keyframes progressGrow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>Animation Properties — Part 2</h1>
<!-- ====== 1. BASIC ANIMATION WITH FILL-MODE ====== -->
<section>
<h2>1. animation-fill-mode: forwards</h2>
<p>The box stays at its final position and color after the animation ends.</p>
<div class="demo-area">
<div class="box"></div>
</div>
<div class="code-block">
.box {
animation-name: exampleAnimation;
animation-duration: 2s;
animation-fill-mode: forwards;
}
@keyframes exampleAnimation {
from { transform: translateX(0); background-color: blue; }
to { transform: translateX(200px); background-color: red; }
}
</div>
<p class="note">Without <code>fill-mode: forwards</code>, the box would snap back to its original position and color.</p>
</section>
<!-- ====== 2. FILL MODE COMPARISON ====== -->
<section>
<h2>2. animation-fill-mode Comparison</h2>
<p>Refresh the page to see the four fill-mode values in action.</p>
<div class="demo-area" style="flex-direction: column; gap: 20px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">none:</span>
<div class="fill-box fill-none">none</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">forwards:</span>
<div class="fill-box fill-forwards">forwards</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">backwards:</span>
<div class="fill-box fill-backwards">backwards</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">both:</span>
<div class="fill-box fill-both">both</div>
</div>
</div>
<div class="code-block">
.fill-none { animation-fill-mode: none; }
.fill-forwards { animation-fill-mode: forwards; }
.fill-backwards { animation-fill-mode: backwards; }
.fill-both { animation-fill-mode: both; }
@keyframes fillDemo {
from { opacity: 0; transform: translateX(-50px); background: #dc3545; }
to { opacity: 1; transform: translateX(0); background: #28a745; }
}
</div>
<p class="note"><strong>none</strong> reverts after the animation. <strong>forwards</strong> keeps the end state. <strong>backwards</strong> applies the start state during the delay. <strong>both</strong> does both.</p>
</section>
<!-- ====== 3. ANIMATION-PLAY-STATE ====== -->
<section>
<h2>3. animation-play-state</h2>
<p>Hover over the box to pause the animation.</p>
<div class="demo-area">
<div class="play-container">
<div class="play-box">Hover to Pause</div>
</div>
</div>
<div class="code-block">
.play-box {
animation: pulseAnim 1.5s ease-in-out infinite;
}
.play-box:hover {
animation-play-state: paused;
}
</div>
</section>
<!-- ====== 4. ANIMATION-COMPOSITION ====== -->
<section>
<h2>4. animation-composition</h2>
<p>Compare how multiple animations combine with different composition values.</p>
<div class="demo-area" style="flex-direction: column; gap: 25px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">replace (default):</span>
<div class="composition-box comp-replace">replace</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">add:</span>
<div class="composition-box comp-add">add</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">accumulate:</span>
<div class="composition-box comp-accumulate">accumulate</div>
</div>
</div>
<div class="code-block">
/* replace: later animation overwrites earlier */
.comp-replace {
animation-name: moveRight, rotateBox;
animation-composition: replace;
}
/* add: effects combine (move right AND up) */
.comp-add {
animation-name: moveRight, moveUp;
animation-composition: add;
}
/* accumulate: scale values add together */
.comp-accumulate {
animation-name: scaleUp, scaleDown;
animation-composition: accumulate;
}
</div>
<p class="note">With <code>add</code>, the box moves both right and up. With <code>replace</code>, only the second animation's transform applies.</p>
</section>
<!-- ====== 5. ANIMATION-TIMELINE (SCROLL) ====== -->
<section>
<h2>5. animation-timeline: scroll()</h2>
<p>Scroll inside the box to see the animation progress with the scroll position.</p>
<div class="scroll-container">
<div class="scroll-content">
<div class="scroll-instruction">⬇️ Scroll down to animate ⬇️</div>
<div class="scroll-animated-box"></div>
<p style="margin-top: 20px; color: #6c757d;">
The box grows, rotates, and changes color as you scroll.
Keep scrolling to see the full animation.
</p>
<p style="margin-top: 200px; color: #6c757d;">
The animation is driven by the scroll position — not by time.
</p>
<p style="margin-top: 200px; color: #6c757d;">
Near the bottom — the animation is almost complete.
</p>
<p style="margin-top: 200px; color: #6c757d;">
🎉 You've reached the end!
</p>
</div>
</div>
<div class="code-block">
.scroll-animated-box {
animation-name: scrollAnimation;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
}
@keyframes scrollAnimation {
from {
transform: scale(0.5) rotate(0deg);
background: #007bff;
opacity: 0.5;
}
to {
transform: scale(1) rotate(360deg);
background: #28a745;
opacity: 1;
}
}
</div>
<div class="tip-box warning">
<strong>⚠️ Browser Support:</strong>
<code>animation-timeline</code> is a modern feature. Check <a href="https://caniuse.com/css-scroll-timeline" target="_blank">caniuse.com</a> for current browser support.
</div>
</section>
<!-- ====== 6. PRACTICAL: HOVER-PAUSE CARDS ====== -->
<section>
<h2>6. Practical Example: Hover-Pause Cards</h2>
<p>Hover over a card to pause its floating animation.</p>
<div class="hover-pause-container">
<div class="hover-pause-card">
<div class="icon">🚀</div>
<h4>Fast</h4>
<p>Hover to pause</p>
</div>
<div class="hover-pause-card">
<div class="icon">🎨</div>
<h4>Beautiful</h4>
<p>Hover to pause</p>
</div>
<div class="hover-pause-card">
<div class="icon">🔒</div>
<h4>Secure</h4>
<p>Hover to pause</p>
</div>
</div>
<div class="code-block">
.hover-pause-card {
animation: floatCard 3s ease-in-out infinite;
}
.hover-pause-card:hover {
animation-play-state: paused;
}
@keyframes floatCard {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
</div>
</section>
<!-- ====== 7. PRACTICAL: SCROLL PROGRESS ====== -->
<section>
<h2>7. Practical Example: Scroll Progress Bar</h2>
<p>Scroll inside the box to see the progress bar fill based on scroll position.</p>
<div class="progress-scroll-container">
<div class="progress-scroll-content">
<div class="progress-indicator">
<div class="progress-fill"></div>
</div>
<p style="margin-top: 20px; color: #6c757d;">
The progress bar at the top fills as you scroll.
</p>
<p style="margin-top: 300px; color: #6c757d;">
Keep scrolling to see the progress grow.
</p>
<p style="margin-top: 300px; color: #6c757d;">
Almost there...
</p>
<p style="margin-top: 300px; color: #6c757d;">
🎉 Complete!
</p>
</div>
</div>
<div class="code-block">
.progress-fill {
animation-name: progressGrow;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
transform-origin: left;
}
@keyframes progressGrow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Animation Properties — Part 2</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>animation-fill-mode</code></td>
<td>Styles before/after animation</td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>animation-play-state</code></td>
<td>Running or paused</td>
<td><code>running</code></td>
</tr>
<tr>
<td><code>animation-timeline</code></td>
<td>Timeline that controls the animation</td>
<td><code>auto</code></td>
</tr>
<tr>
<td><code>animation-composition</code></td>
<td>How multiple animations combine</td>
<td><code>replace</code></td>
</tr>
</table>
<h3>animation-fill-mode Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No styles applied outside the animation (default)</td>
</tr>
<tr>
<td><code>forwards</code></td>
<td>Keep the last keyframe's styles</td>
</tr>
<tr>
<td><code>backwards</code></td>
<td>Apply the first keyframe's styles during delay</td>
</tr>
<tr>
<td><code>both</code></td>
<td>Apply both forwards and backwards</td>
</tr>
</table>
<h3>animation-play-state Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>running</code></td>
<td>Animation is playing (default)</td>
</tr>
<tr>
<td><code>paused</code></td>
<td>Animation is paused</td>
</tr>
</table>
<h3>animation-timeline Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Default document timeline (default)</td>
</tr>
<tr>
<td><code>none</code></td>
<td>No timeline — animation doesn't play</td>
</tr>
<tr>
<td><code>scroll()</code></td>
<td>Binds animation to scroll position</td>
</tr>
<tr>
<td><code>view()</code></td>
<td>Binds animation to element visibility in viewport</td>
</tr>
</table>
<h3>animation-composition Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>replace</code></td>
<td>New animations overwrite previous ones (default)</td>
</tr>
<tr>
<td><code>add</code></td>
<td>New animations add to existing effects</td>
</tr>
<tr>
<td><code>accumulate</code></td>
<td>Similar to add, with additional rules for keyframes</td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>animation-fill-mode: forwards</code> to keep the end state</li>
<li>Use <code>animation-fill-mode: backwards</code> to apply start styles during delay</li>
<li>Use <code>animation-play-state: paused</code> on hover for interactive control</li>
<li>Use <code>animation-timeline: scroll()</code> for scroll-driven animations</li>
<li>Use <code>animation-composition: add</code> to combine transform effects</li>
<li>Always provide fallbacks for modern features</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't forget <code>animation-fill-mode</code> — elements snap back without it</li>
<li>Don't use <code>animation-timeline</code> without a fallback for unsupported browsers</li>
<li>Don't confuse <code>animation-composition: add</code> with <code>replace</code></li>
<li>Don't pause animations that are essential for feedback</li>
<li>Don't forget that scroll-driven animations need a scrollable container</li>
<li>Don't forget about <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box,
.fill-box,
.play-box,
.composition-box,
.scroll-animated-box,
.hover-pause-card {
animation: none;
}
}
/* Scroll-driven animation with fallback */
.scroll-animated-box {
animation-name: scrollAnimation;
animation-duration: 1s;
animation-fill-mode: both;
}
@supports (animation-timeline: scroll()) {
.scroll-animated-box {
animation-timeline: scroll(nearest);
}
}
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default |
|---|---|---|
animation-fill-mode | Styles before/after animation | none |
animation-play-state | Running or paused | running |
animation-timeline | Timeline that controls the animation | auto |
animation-composition | How multiple animations combine | replace |
animation-fill-mode Values
| Value | Description |
|---|---|
none | No styles applied outside the animation (default) |
forwards | Keep the last keyframe’s styles |
backwards | Apply the first keyframe’s styles during delay |
both | Apply both forwards and backwards |
animation-play-state Values
| Value | Description |
|---|---|
running | Animation is playing (default) |
paused | Animation is paused |
animation-timeline Values
| Value | Description |
|---|---|
auto | Default document timeline (default) |
none | No timeline — animation doesn’t play |
scroll() | Binds animation to scroll position |
view() | Binds animation to element visibility |
animation-composition Values
| Value | Description |
|---|---|
replace | New animations overwrite previous ones (default) |
add | New animations add to existing effects |
accumulate | Similar to add, with additional rules for keyframes |
Best Practices
✅ Do This:
/* Keep the end state */
.box {
animation-fill-mode: forwards;
}
/* Pause on hover */
.box:hover {
animation-play-state: paused;
}
/* Scroll-driven animation with fallback */
@supports (animation-timeline: scroll()) {
.box {
animation-timeline: scroll();
}
}
/* Combine transforms */
.box {
animation-name: moveRight, rotate;
animation-composition: add;
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box {
animation: none;
}
}
❌ Don’t Do This:
/* Don't forget fill-mode */
.box {
animation: slideIn 1s;
/* Snaps back after animation */
}
/* Don't use animation-timeline without fallback */
.box {
animation-timeline: scroll();
/* Won't work in unsupported browsers */
}
/* Don't use replace when you want combined effects */
.box {
animation-name: moveRight, moveUp;
animation-composition: replace;
/* Only moveUp applies — moveRight is overwritten */
}
Pro Tip: animation-fill-mode: forwards is essential for entrance animations — without it, elements snap back to their original state. animation-play-state: paused on hover gives users control over animations, which is great for accessibility. animation-timeline: scroll() enables scroll-driven animations — a modern, performant way to create engaging scroll experiences (always provide a fallback). animation-composition: add lets you combine multiple transforms (e.g., move right AND rotate). And always respect prefers-reduced-motion — some users experience discomfort from animations!
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!