devnotes.

CSS Animations & Keyframes

While transition only animates between two states (normal and hover, for example), @keyframes defines a multi-step animation sequence that can run automatically, repeat, and reverse — without needing a triggering event.

@keyframes bounce {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-20px); }
  100% { transform: translateY(0); }
}

.ball {
  animation: bounce 1s ease-in-out infinite;
}

@keyframes defines named checkpoints (0%, 50%, 100%, or from/to for just two steps) describing the element's style at each point in time; the browser smoothly interpolates between them.

The animation shorthand combines several properties:

.element {
  animation: bounce 1s ease-in-out infinite;
  /* name | duration | timing-function | iteration-count */

  animation-name: bounce;
  animation-duration: 1s;
  animation-iteration-count: infinite; /* or a specific number like 3 */
  animation-direction: alternate;       /* plays forward then backward */
  animation-fill-mode: forwards;        /* keeps the final keyframe's styles after finishing */
}
  • iteration-count — how many times the animation repeats; infinite loops forever, useful for loading spinners.
  • direction: alternate — plays forward, then backward, then forward again, avoiding an abrupt jump back to the start on each loop.
  • fill-mode: forwards — without it, an element snaps back to its pre-animation styles the instant the animation ends, which can look like a glitch for entrance animations meant to leave the element in its final state.

A practical rule of thumb: use transition for simple two-state changes triggered by user interaction (hover, focus, a class toggle), and reach for @keyframes when you need a self-running, multi-step, or looping animation — like a spinner, a pulsing badge, or an entrance effect that plays on page load.