Why Is My CSS Animation Not Playing?

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.

Error 1

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

No matching keyframes
Animation target The element asks for slideIn, but the keyframes are named slide-in.
The animation does not play because the animation name and keyframes name are different.

Correct code

Names match
.box {
  animation: slideIn .6s ease;
}

@keyframes slideIn {
  from { transform: translateX(0); }
  to { transform: translateX(120px); }
}

Fixed visual result

Keyframes connected
Animation target The animation name now points to the exact keyframes block.
Once the names match, the browser knows which keyframes to run.
Error 2

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

No visible playback time
animation-name: pulse set
animation-duration missing
The browser has a keyframe name, but no real time to show the animation.

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

Animation has time
animation-name: pulse set
animation-duration: .8s visible
A real duration gives the browser time to interpolate between keyframe states.
Error 3

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

Snaps back after ending
Animated box The animation can play, then the element returns to its original non-animated style.
Without fill mode, the end state is not preserved after the animation finishes.

Correct code

Keep final state
.box {
  animation: slideIn .6s ease forwards;
}

@keyframes slideIn {
  from { transform: translateX(0); }
  to { transform: translateX(120px); }
}

Fixed visual result

Final state remains
Animated box forwards keeps the element at the final keyframe after playback.
Use animation-fill-mode: forwards when the final state should remain visible.
Error 4

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

Rule misses target
HTML class: loader-dot real
CSS selector: .loading-dot misses
The selector is valid CSS, but it does not match the actual element.

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

Rule reaches target
HTML class: loader-dot real
CSS selector: .loader-dot matches
The keyframes can run because the animation rule is attached to the real element.
Premium pattern

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

Predictable animation
Production-ready notice

The animation has a matching name, visible duration, clear start/end states, fill mode, and a reduced-motion fallback.

Premium animation is not just motion. It is clear state management with a respectful 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-name exactly matches the @keyframes name.
  • 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: forwards or both when the final state should remain.
  • Check whether prefers-reduced-motion or another rule disables the animation.
  • If the CSS change does not appear at all, check cache and stylesheet loading.
Best first move Inspect the target element and confirm the browser sees an active animation rule.
Most common cause The animation name and the @keyframes name do not match exactly.
Most invisible cause The animation technically runs, but the start and end states look almost the same.
Better mindset Keyframes describe motion, but the element still needs a valid animation rule to run them.

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.

Want more fixes like this?

Browse more CSS motion and interaction debugging guides in the FrontFixer library.

Why Is My CSS Transition Not Working?

A CSS transition usually stops working when the transition is placed on the wrong state, the property cannot be animated, the starting value is missing, or another CSS rule overrides the change.

CSS Animation Fix

Why is my CSS transition not working?

A CSS transition can look broken even when the syntax seems correct. The element may jump instantly, the hover state may change without animation, the accordion may open suddenly, or the effect may work in one direction but not the other. The real cause is usually one of a few things: the transition is on the wrong selector, the property cannot animate smoothly, the browser has no starting value, or another rule is overriding the final state.

  • CSS transition
  • Hover animation
  • Animatable properties
  • Motion debugging

What the bug looks like

The element changes instantly, only animates one way, ignores the transition, or jumps when it should slide, fade, lift, or expand smoothly.

Why it happens

CSS transitions only animate between known values for animatable properties. If the property cannot interpolate, there is nothing to animate.

What usually fixes it

Put transitions on the base state, animate transform or opacity when possible, define clear start/end values, and avoid transitioning display.

Error 1

The transition is only written on the hover state

This is one of the most common transition mistakes. If the transition is only declared inside :hover, the browser may animate when entering hover but snap back when leaving. The base element should know how to transition.

Broken code

Transition on hover only
.card:hover {
  transform: translateY(-6px);
  background: #fff5ef;
  transition: transform .2s ease;
}

Broken visual result

Snaps back
Card hover The card changes, but the reverse movement can feel abrupt because the base state has no transition.
Jump
The transition belongs on the normal state, not only on the changed state.

Correct code

Transition on base element
.card {
  transition:
    transform .2s ease,
    background .2s ease;
}

.card:hover {
  transform: translateY(-6px);
  background: #fff5ef;
}

Fixed visual result

Smooth both ways
Card hover The base element owns the transition, so the browser can animate in and out of the state.
Smooth
Put the transition on the element before the state changes.
Error 2

You are trying to transition display

display:none to display:block is not a smooth animation. The element is either in the layout or not. The browser cannot gradually interpolate between those two states.

Broken code

Display cannot fade
.menu {
  display: none;
  transition: display .2s ease;
}

.nav:hover .menu {
  display: block;
}

Broken visual result

Appears instantly
display: none → block not smooth
transition: display .2s ease bad target
The browser cannot animate display like opacity or transform.

Correct code

Opacity and transform
.menu {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition:
    opacity .2s ease,
    transform .2s ease;
}

.nav:hover .menu {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

Fixed visual result

Fades and slides
opacity: 0 → 1 smooth
transform: translateY(8px) → 0 smooth
Animate properties the browser can interpolate smoothly.
Error 3

The changed property is not included in the transition

If you change transform but only transition background, the color may animate while the movement jumps instantly. The transition list has to include the property that changes.

Broken code

Wrong property listed
.box {
  transition: background .2s ease;
}

.box:hover {
  transform: translateX(80px);
  background: #ff6a3d;
}

Broken visual result

Movement jumps
Box movement The background can animate, but the transform is not in the transition list.
Jump
The property that changes must be part of the transition.

Correct code

Transition real changes
.box {
  transition:
    transform .2s ease,
    background .2s ease;
}

.box:hover {
  transform: translateX(80px);
  background: #ff6a3d;
}

Fixed visual result

Movement is smooth
Box movement Now both the movement and the background color have transition instructions.
Smooth
Transition the properties you actually change.
Error 4

The property causes layout jumps instead of smooth motion

Some properties can animate, but they are expensive or visually awkward because they force layout recalculation. Animating width, height, margin, or top can feel jumpy. For motion, transform is usually safer.

Broken code

Layout-heavy animation
.badge {
  width: 88px;
  transition: width .2s ease;
}

.badge:hover {
  width: 170px;
}

Broken visual result

Layout gets pushed
Expanding badge The element grows by changing layout size, which can push nearby content.
Wide
Changing layout dimensions can create visual instability around the element.

Correct code

Transform instead
.badge {
  transform: scale(1);
  transition: transform .2s ease;
}

.badge:hover {
  transform: scale(1.08);
}

Fixed visual result

Motion without layout push
Scaling badge The element feels animated without forcing surrounding layout to recalculate.
Scale
For motion, prefer transform and opacity when they fit the design.
Premium pattern

A production-minded transition pattern

A better transition setup is intentional. It defines a stable base state, animates predictable properties, respects reduced motion preferences, and avoids layout-heavy changes unless they are truly necessary.

Premium code

Smooth and safer
.card {
  transform: translateY(0);
  opacity: 1;
  border-color: #e5e7eb;
  box-shadow: 0 12px 28px rgba(15,23,42,.06);
  transition:
    transform .2s ease,
    border-color .2s ease,
    box-shadow .2s ease;
}

.card:hover,
.card:focus-within {
  transform: translateY(-4px);
  border-color: #ffd2c2;
  box-shadow: 0 18px 38px rgba(255,106,61,.12);
}

@media (prefers-reduced-motion: reduce) {
  .card {
    transition: none;
  }

  .card:hover,
  .card:focus-within {
    transform: none;
  }
}

Premium visual result

Predictable motion
Production-ready transition

The card has a clear base state, a clear hover/focus state, smooth visual feedback, and a reduced-motion fallback.

Premium transitions are not just decorative. They are controlled, readable, and less likely to create layout instability.

Fast practical rule

If a CSS transition is not working, inspect whether the property actually changes. Then check whether that property is animatable, included in the transition list, and declared from a clear starting value on the base element.

Debug checklist

  • Put transition on the base element, not only on :hover.
  • Confirm the property you are changing is included in the transition list.
  • Check whether the property can actually animate smoothly.
  • Avoid trying to transition display:none to display:block.
  • Use opacity, transform, or max-height patterns when appropriate.
  • Define a clear starting value and ending value.
  • Check DevTools to confirm no stronger rule overrides the hover or active state.
  • Use prefers-reduced-motion for more respectful production UI.
Best first move Force the hover or active state in DevTools and check whether the property really changes.
Most common cause The transition is declared only on the changed state instead of the base element.
Most confusing cause The CSS is valid, but the property cannot animate the way you expect.
Better mindset A transition does not create a state change. It only smooths a state change that already exists.

Final takeaway

A CSS transition not working usually means the browser has no smooth path between two values. Either the transition is on the wrong selector, the wrong property is listed, the property cannot animate, or the final state is being overridden.

Start by proving that the property actually changes. Then make sure the base element owns the transition and the property can be interpolated. Once those pieces are correct, the transition becomes predictable instead of mysterious.

Want more fixes like this?

Browse more CSS interaction and layout debugging guides in the FrontFixer library.