A CSS animation usually does not play when the animation name does not match the keyframes, the duration is missing, the element is not receiving the animation rule, or the animation finishes instantly and returns to its starting style.
CSS Animation Fix
Why is my CSS animation not playing?
A CSS animation can fail in a way that feels invisible. You write @keyframes, add an animation name, refresh the page, and nothing moves. Sometimes the animation never starts. Sometimes it plays too fast to notice. Sometimes it plays once and snaps back. Most of the time, the browser is not ignoring your CSS. The animation setup is missing one small but essential piece.
- @keyframes bugs
- Animation name mismatch
- Duration problems
- Fill mode issues
What the bug looks like
The element stays still, moves for a split second, jumps back, or seems to ignore the @keyframes block completely.
Why it happens
CSS animations need a matching keyframe name, a duration, a target element, and visible changes between frames.
What usually fixes it
Match the animation name, define duration, verify the selector, use visible keyframes, and add fill mode when the final state should remain.
The animation name does not match the keyframes name
This is the first thing to check. CSS animation names are literal. A small typo, different capitalization, or different word is enough to stop the animation from connecting to the keyframes.
Broken code
Name mismatch.box {
animation: slideIn .6s ease;
}
@keyframes slide-in {
from { transform: translateX(0); }
to { transform: translateX(120px); }
}
Broken visual result
slideIn, but the keyframes are named slide-in.
Correct code
Names match.box {
animation: slideIn .6s ease;
}
@keyframes slideIn {
from { transform: translateX(0); }
to { transform: translateX(120px); }
}
Fixed visual result
The animation has no visible duration
An animation needs time. If the duration is missing, zero, or too tiny to notice, the browser may apply the animation instantly. That can look like the animation never played.
Broken code
No duration.box {
animation-name: pulse;
}
@keyframes pulse {
from { opacity: .4; }
to { opacity: 1; }
}
Broken visual result
Correct code
Duration included.box {
animation-name: pulse;
animation-duration: .8s;
animation-timing-function: ease;
}
@keyframes pulse {
from { opacity: .4; }
to { opacity: 1; }
}
Fixed visual result
The animation plays once and snaps back
Sometimes the animation does play, but the final state disappears immediately after it ends. By default, an element returns to its normal style when the animation finishes.
Broken code
No fill mode.box {
animation: slideIn .6s ease;
}
@keyframes slideIn {
from { transform: translateX(0); }
to { transform: translateX(120px); }
}
Broken visual result
Correct code
Keep final state.box {
animation: slideIn .6s ease forwards;
}
@keyframes slideIn {
from { transform: translateX(0); }
to { transform: translateX(120px); }
}
Fixed visual result
forwards keeps the element at the final keyframe after playback.
animation-fill-mode: forwards when the final state should remain visible.The selector never applies the animation to the element
A keyframes block sitting in your stylesheet does nothing by itself. Some element must receive the animation rule. If the selector is wrong, the animation exists but never gets attached to the target.
Broken code
Wrong selector<div class="loader-dot"></div>
.loading-dot {
animation: bounce .8s ease infinite;
}
@keyframes bounce {
50% { transform: translateY(-12px); }
}
Broken visual result
Correct code
Selector matches<div class="loader-dot"></div>
.loader-dot {
animation: bounce .8s ease infinite;
}
@keyframes bounce {
50% { transform: translateY(-12px); }
}
Fixed visual result
A production-minded CSS animation pattern
A stronger animation setup is explicit. It uses a matching keyframe name, a visible duration, a clear timing function, a final-state decision, and a reduced-motion fallback for users who prefer less movement.
Premium code
Clear animation setup.notice {
animation-name: noticeEnter;
animation-duration: .55s;
animation-timing-function: ease;
animation-fill-mode: both;
}
@keyframes noticeEnter {
from {
opacity: 0;
transform: translateY(12px) scale(.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@media (prefers-reduced-motion: reduce) {
.notice {
animation: none;
}
}
Premium visual result
The animation has a matching name, visible duration, clear start/end states, fill mode, and a reduced-motion fallback.
Fast practical rule
If a CSS animation is not playing, first check whether the element actually has an animation-name in DevTools. Then confirm that the name matches an existing @keyframes block and that the animation has a visible duration.
Debug checklist
- Confirm the
animation-nameexactly matches the@keyframesname. - Check capitalization, hyphens, and spelling in the animation name.
- Make sure the animation has a duration longer than zero.
- Verify that the CSS selector actually matches the target element.
- Inspect the element in DevTools to see whether the animation rule is active or crossed out.
- Make sure the keyframes create a visible difference between states.
- Use
animation-fill-mode: forwardsorbothwhen the final state should remain. - Check whether
prefers-reduced-motionor another rule disables the animation. - If the CSS change does not appear at all, check cache and stylesheet loading.
@keyframes name do not match exactly.
Final takeaway
A CSS animation not playing usually has a simple reason: the keyframes name does not match, the duration is missing, the selector misses the element, or the animation runs but does not create a visible change.
Start by proving the animation rule is active on the element. Then check the keyframes name, duration, visible frame changes, and fill mode. Once those pieces line up, the animation becomes much easier to debug.