CSS@keyframes reference

@keyframes Reference

@keyframes defines the waypoints of a CSS animation: what the styles look like at each moment of the timeline. The animation property on an element then plays that timeline. This page is a complete reference for keyframe syntax — selectors, multiple stops, per-keyframe timing, what can and cannot be animated, and how custom properties make one keyframe set serve many elements.

Basic Syntax

CSS
@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.modal {
  animation: fade-in 300ms ease-out;
}

The name (fade-in above) is a custom identifier you invent. It is case-sensitive, cannot be a CSS keyword like none or initial, and if two @keyframes share a name, the last one defined wins entirely — they do not merge.

from/to vs Percentages

from is an alias for 0% and to for 100%. Percentages unlock any number of intermediate stops:

CSS
/* Equivalent pairs */
@keyframes grow-a { from { scale: 0; } to { scale: 1; } }
@keyframes grow-b { 0%   { scale: 0; } 100% { scale: 1; } }

/* Multiple stops */
@keyframes bounce-in {
  0%   { scale: 0.3; opacity: 0; }
  50%  { scale: 1.05; }
  70%  { scale: 0.95; }
  100% { scale: 1; opacity: 1; }
}
Omitted endpoints
If you omit 0% or 100%, the browser uses the element's current computed styles for that endpoint. That is often exactly what you want: define only the starting state and let the element animate to wherever its normal CSS puts it.
Sharing Styles Between Stops

CSS
/* Comma-separate selectors that share declarations */
@keyframes pulse {
  0%, 100% { scale: 1; opacity: 1; }
  50%      { scale: 1.08; opacity: 0.85; }
}

/* Classic loading-dots stagger */
@keyframes blink {
  0%, 80%, 100% { opacity: 0.2; }
  40%           { opacity: 1; }
}

.dot { animation: blink 1.4s infinite both; }
.dot:nth-child(2) { animation-delay: 0.2s; }
.dot:nth-child(3) { animation-delay: 0.4s; }
What Can Be Animated?

Only animatable properties interpolate. A property is animatable when the browser can compute in-between values: numbers, lengths, percentages, colors, transforms. Discrete properties (like display: block to flex) jump at the 50% mark instead of blending.

Category

Examples

Animates?

Transform & opacity

transform, translate, scale, rotate, opacity

Yes — GPU-friendly, prefer these

Colors

color, background-color, border-color

Yes — interpolates channels

Box geometry

width, height, margin, padding, top/left

Yes — but triggers layout every frame

Shadows & filters

box-shadow, filter, backdrop-filter

Yes — can be paint-heavy

Discrete values

display, visibility, font-family

Flips, no interpolation

Custom properties

--anything

Only if registered with @property

Performance rule
Animating transform and opacity can run entirely on the compositor at 60fps. Animating width,height, or top forces layout recalculation on every frame. When possible, express motion as transforms.
Timing Per Keyframe

animation-timing-function set on the element applies between each pair of keyframes, not across the whole animation. You can also override the easing inside a keyframe — it controls the segment leaving that keyframe:

CSS
@keyframes drop {
  0% {
    translate: 0 -200px;
    /* easing for the 0% → 60% segment */
    animation-timing-function: ease-in;
  }
  60% {
    translate: 0 0;
    /* easing for the 60% → 100% segment */
    animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  100% {
    translate: 0 0;
  }
}
Note
Properties that are NOT allowed inside keyframes:animation-name, animation-duration,animation-delay, andanimation-iteration-count. Onlyanimation-timing-function can be tuned per keyframe.
Reusing Keyframes Across Elements

A keyframe set is a reusable definition — many elements can play the same timeline with different durations, delays, and directions:

CSS
@keyframes float {
  from { translate: 0 0; }
  to   { translate: 0 -12px; }
}

.cloud-small { animation: float 3s ease-in-out infinite alternate; }
.cloud-large { animation: float 5s ease-in-out 0.7s infinite alternate; }

/* One element can also run several keyframe sets at once */
.spinner {
  animation:
    spin 1.2s linear infinite,
    fade-in 300ms ease-out;
}
Dynamic Values with Custom Properties

Keyframes can read custom properties, which turns a single keyframe set into a parameterized animation. Each element supplies its own values:

CSS
@keyframes slide-up {
  from {
    translate: 0 var(--slide-distance, 40px);
    opacity: 0;
  }
  to {
    translate: 0 0;
    opacity: 1;
  }
}

.card        { --slide-distance: 24px; animation: slide-up 400ms ease-out; }
.hero-title  { --slide-distance: 80px; animation: slide-up 700ms ease-out; }

To animate the custom property itself (rather than reading it), you must register it with @property so the browser knows its type:

CSS
@property --glow {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

@keyframes charge {
  to { --glow: 100%; }
}

.badge {
  background: radial-gradient(circle, gold var(--glow), transparent 0);
  animation: charge 2s ease-in-out infinite alternate;
}
Keyframes and the Cascade
  • Keyframe declarations ignore !important — an !important inside a keyframe is dropped entirely.

  • While an animation runs, its values override normal declarations (they sit above the author origin), except properties the element sets with !important.

  • If several keyframe sets animate the same property on one element, the last animation listed wins for that property.

  • Keyframe names are scoped per cascade layer and can be shadowed — keep names unique to stay sane.

Complete Worked Example

CSS
@keyframes toast-in {
  0% {
    translate: 0 100%;
    opacity: 0;
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
  70% {
    translate: 0 -6px;
    opacity: 1;
  }
  100% {
    translate: 0 0;
  }
}

.toast {
  animation: toast-in 450ms both;
}

@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}
Tip
animation-fill-mode: both (the both keyword in the shorthand) applies the first keyframe during any delay and keeps the last keyframe after the animation ends — the sensible default for enter animations.
Key Takeaways
  • from/to equal 0%/100%; percentages give unlimited intermediate stops, and stops can share declarations via commas.

  • Omitted endpoints fall back to the element’s current styles.

  • Prefer transform and opacity — they animate on the compositor without layout work.

  • animation-timing-function applies per segment and can be overridden inside individual keyframes.

  • One keyframe set serves many elements; custom properties (plus @property) parameterize it.

  • Always provide a reduced-motion path.