skip to content

Keyframe Animations

@keyframes plus the animation properties give you multi-step, looping, self-starting motion with no trigger required. Interviewers ask for the difference from transitions and what animation-fill-mode actually does to the before/after state.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

6

In CSS, what can a @keyframes animation express that a transition cannot, and what starts each of them?

level: juniorimportance: must knowfreq 78%

answer

  1. one reacts, one self-starts
  2. transition needs a value change
  3. percentages give you the middle
  4. loop, alternate, pause: animations only
  5. fill-mode has no transition equivalent

basics

~20 s

A CSS transition interpolates between two states and only runs when a value actually changes, typically on :hover or a class toggle. A @keyframes animation declares many intermediate stops, starts on its own, and can loop, alternate direction, and be paused.

solid answer

~50 s

Both interpolate values over time, but they are triggered differently and can express different shapes of motion. A transition is reactive: you declare `transition: opacity 300ms ease`, and the browser interpolates only when the cascade gives that element a new value for `opacity` — a class toggle, a `:hover`, a media-query change. No change, no motion, and it always runs exactly once from the old value to the new one. An animation is declarative: the moment an element's computed `animation-name` points at a `@keyframes` rule, the animation starts by itself, with no interaction and no state change — which is why loading spinners and attention pulses are animations, not transitions. `@keyframes` also lets you declare intermediate stops at any percentage, so motion can overshoot, pause mid-way, or take a non-monotonic path. Only animations get `animation-iteration-count: infinite`, `animation-direction: alternate`, `animation-fill-mode`, and `animation-play-state: paused`.

code

css · 16 lines
css
.button {
  background-color: #1a4433;
  transition: background-color 200ms ease-out;
}
.button:hover {
  background-color: #2c6a52;
}

@keyframes pulse {
  0%   { opacity: 1; }
  50%  { opacity: 0.4; }
  100% { opacity: 1; }
}
.badge {
  animation: pulse 1.2s ease-in-out infinite;
}

go deeper

for a junior

Be ready to say plainly that a transition only runs when a value changes, while a @keyframes animation starts on its own and can repeat. Naming the trigger for each is most of the answer.

for a middle

Explain the mechanics: percentage stops, iteration-count, direction, fill-mode and play-state, and why a transition physically cannot express a mid-point value without chaining a second one.

for a senior

Show judgment about which to reach for in a real UI — reversible interaction feedback versus self-starting or looping motion — and mention that an interrupted transition reverses smoothly while a restarted animation snaps to its schedule.

for a principal

Own the house rule: where motion lives in the design system, which mechanism each category of motion uses, and how the team avoids two mechanisms driving one property, which is a class of bug that is very hard to see in review.

## Two mechanisms, one job CSS has exactly two built-in ways to change a value over time. A **transition** watches properties on an element and, when the cascade hands that element a *new* computed value for one of them, interpolates from the old value to the new one. A **keyframe animation** is a named script of states — a `@keyframes` rule — that the element plays from start to finish for as long as `animation-name` points at it. That difference in triggering is the whole story. A transition needs something to change. An animation needs nothing at all. ## What starts a transition Anything that changes the computed value of a transitioned property: a `:hover` or `:focus` state, a class added or removed, a media query flipping, a custom property being reassigned. The transition is defined on the element in its *resting* state so that it applies in both directions. ```css .button { background-color: #1a4; transition: background-color 200ms ease-out; } .button:hover { background-color: #2c6; } ``` Remove the `:hover` rule and nothing ever moves — the declaration is inert until a value changes. ## What starts an animation Nothing external. As soon as the element's computed `animation-name` resolves to a `@keyframes` rule, the animation begins (after `animation-delay`). It runs on page load, on insertion into the document, on the element becoming rendered. ```css @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } } .badge { animation: pulse 1.2s ease-in-out infinite; } ``` A transition cannot produce this. It has only two endpoints — the old value and the new one — so a fade that dips to 0.4 and comes back would require reacting to `transitionend` and setting a second value, which is a chain of transitions, not one. ## The keyframe selectors Inside `@keyframes`, stops are written as percentages of the total duration, and `from` / `to` are aliases for `0%` and `100%`. Several offsets can share one block: ```css @keyframes shake { 0%, 100% { margin-left: 0; } 25%, 75% { margin-left: -6px; } 50% { margin-left: 6px; } } ``` The percentages are positions in time, not in space, and they need not be listed in order — the browser sorts them. ## The properties that only animations have - `animation-name` — which `@keyframes` rule to play; `none` stops it. - `animation-duration` — how long one iteration takes. It defaults to `0s`, so an animation with no duration produces no visible motion. - `animation-timing-function` — the easing curve, per segment between keyframes. - `animation-delay` — how long to wait before starting. A *negative* delay starts the animation immediately, already partway through, which is the standard way to stagger many copies of one animation. - `animation-iteration-count` — a number, a fraction, or `infinite`. - `animation-direction` — `normal`, `reverse`, `alternate`, `alternate-reverse`. - `animation-fill-mode` — whether keyframe values apply before the start and after the end. - `animation-play-state` — `running` or `paused`. There is no CSS equivalent for a transition; you cannot pause one. All of these collapse into the `animation` shorthand, and multiple animations can be layered as a comma-separated list. ## During the run While an animation is running, its interpolated values apply above normal author declarations, so a plain rule setting the same property does not visibly win, however specific it is. When the animation ends with the default `animation-fill-mode: none`, the element snaps back to whatever the cascade says — which surprises people expecting the last keyframe to stick. ## Choosing between them Reach for a transition when there is a genuine before and after state and the user's action is the trigger: hovers, open/closed panels, focus rings, theme switches. Reach for `@keyframes` when the motion is self-starting (spinners, skeleton shimmers, entrance animations), when it repeats, or when it needs stops in the middle. When both would work — a one-shot fade-in on load, for instance — the animation is usually simpler, because a transition would need a value to change first, which means a second style write or a class added after mount.

  • Could you fade an element in on page load using only a transition?
    Not with CSS alone in one pass. A transition needs the computed value to change after the element is already rendered with the old value, so you would have to add a class in a later frame from script. A `@keyframes` animation with `animation: fade-in 300ms` needs no trigger at all, which is why it is the normal answer for load-time motion.
  • Both a transition and an animation target opacity on the same element at the same time. Which value shows?
    The transition. In the cascade, animated values apply above normal author declarations, and transitioned values apply above everything — including `!important` author rules. In practice this is a smell: two mechanisms driving one property means the animation's keyframe values will look ignored for as long as the transition is active.
  • When would you prefer a transition even though an animation could do the job?
    When the motion is a direct response to an interaction that can reverse mid-flight. A hover transition interrupted halfway smoothly reverses from wherever it is, because the browser just interpolates from the current computed value to the new one. An animation restarted or reversed by swapping `animation-direction` jumps to the keyframe schedule instead, which reads as a glitch.

saying these in an interview costs you the question

  • Claiming transitions can loop with an iteration count
  • Thinking @keyframes needs a hover or class to start
  • Saying transitions support intermediate percentage stops
  • Assuming the last keyframe stays applied by default
  • Treating animation and transition as interchangeable syntax

context

open as a page

In CSS, what does animation-fill-mode do, and what are the practical differences between none, forwards, backwards, and both?

level: middleimportance: must knowfreq 62%

basics

~20 s

animation-fill-mode decides whether keyframe values apply outside the animation's active run. The default none applies nothing before or after; forwards holds the final keyframe after it ends; backwards applies the first keyframe during the delay; both does each.

open as a page

Which events does a CSS keyframe animation dispatch, and when does animationend fail to fire?

level: middleimportance: should knowfreq 36%

basics

~20 s

A CSS animation dispatches animationstart, animationiteration between iterations, animationend on completion, and animationcancel if it stops early. animationend never fires when animation-iteration-count is infinite, or when the animation is removed or the element stops being rendered.

open as a page

In a CSS @keyframes rule, what supplies the starting value if you only declare a to stop, and what happens to a property that appears in some keyframes but not others?

level: middleimportance: should knowfreq 42%

basics

~20 s

The browser synthesises the missing 0% or 100% stop from the element's own computed value for each animated property. A property declared in only some keyframes interpolates between the stops that do declare it, using that computed value as the implicit endpoint.

open as a page

In the CSS animation shorthand, how are two time values interpreted, and what happens to the longhands you leave out?

level: middleimportance: should knowfreq 46%

basics

~20 s

In the animation shorthand the first time value is animation-duration and the second is animation-delay, regardless of their size. Every longhand you omit is reset to its initial value, so a shorthand written later silently wipes an earlier animation-fill-mode or animation-play-state.

open as a page

Removing a CSS animation class from an element and immediately re-adding it in the same script task does not replay the animation. Why, and what actually restarts it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

A keyframe animation is created when an element's computed animation-name gains a value and destroyed when it loses one. Remove and re-add in one task and the computed value is identical at the next style recalculation, so the browser sees no change and no new animation starts.

open as a page