animation-play-state pauses or resumes an animation’s timeline. Resuming continues from the paused position instead of restarting the keyframes.
Start paused and resume with a checkbox
The animation is already assigned but paused. Check the box to run; uncheck to pause wherever the square has reached. Unlike removing animation-name, this retains elapsed progress.
HTML and CSS
HTML
<input class="run" id="run" type="checkbox" /><label for="run"
>Run animation; uncheck to pause</label
>
<div class="stage"><div class="box" aria-hidden="true"></div></div>
CSS
@keyframes travel {
from {
transform: translateX(0);
}
to {
transform: translateX(100px);
}
}
.stage {
background: #eff6ff;
padding: 1rem;
}
.box {
width: 50px;
height: 35px;
background: #2563eb;
margin-block: 0.5rem;
}
label {
display: inline-block;
padding: 0.75rem;
}
.box {
animation: travel 2s linear infinite alternate;
animation-play-state: paused;
}
.run:checked ~ .stage .box {
animation-play-state: running;
}
@media (prefers-reduced-motion: reduce) {
.run:checked ~ .stage .box {
animation-play-state: paused;
}
}
Edit HTML or CSS to update the preview as you type. Copy code snippet copies the selected tab. Preview spacing and font defaults are not included.
Use an explicit pause button
The button changes the actual play state and its label. The decorative square carries no progress or loading semantics. Reduced-motion keeps it paused and reports that preference.
HTML and CSS
HTML
<button type="button" id="toggle">Run animation</button>
<div class="stage"><div class="box" aria-hidden="true"></div></div>
<output aria-live="polite">Paused</output>
<script>
const box = document.querySelector(".box"); const button = document.getElementById("toggle"); const output = document.querySelector("output"); let running = false; const preference = matchMedia("(prefers-reduced-motion: reduce)"); preference.addEventListener("change", event => { if (event.matches) { running = false; box.style.animationPlayState = "paused"; button.textContent = "Run animation"; output.textContent = "Paused: reduced-motion preference"; } }); button.onclick = () => { if (matchMedia("(prefers-reduced-motion: reduce)").matches) { output.textContent = "Paused: reduced-motion preference"; return; } running = !running; box.style.animationPlayState = running ? "running" : "paused"; button.textContent = running ? "Pause animation" : "Run animation"; output.textContent = running ? "Running" : "Paused"; };
</script>
CSS
@keyframes travel {
from {
transform: translateX(0);
}
to {
transform: translateX(100px);
}
}
.stage {
background: #eff6ff;
padding: 1rem;
}
.box {
width: 50px;
height: 35px;
background: #2563eb;
margin-block: 0.5rem;
}
label {
display: inline-block;
padding: 0.75rem;
}
.box {
animation: travel 2s linear infinite alternate;
animation-play-state: paused;
}
Edit HTML or CSS to update the preview as you type. Copy code snippet copies the selected tab. Preview spacing and font defaults are not included.
Values and defaults
The initial value is running and the property is not inherited. Paused stops progression, including during a delay. Running resumes from the retained point. A comma-separated list follows animation-name order; a shorter state list repeats.
Common mistakes
A bare progress bar with no control does not demonstrate pausing. Hover-only pause is unavailable to many keyboard/touch users. Pausing is not removing the animation or resetting it, and a later animation shorthand can reset the play state to running. Infinite motion needs a reachable stop/pause mechanism and a reduced-motion alternative; do not pretend decorative movement measures real work.
Learn animation-name, animation-iteration-count, and animation-delay.
Sources and browser support
MDN: animation-play-state describes the syntax and links to the specification and compatibility data. Check individual values in that table when supporting older browsers.