Code

CSS

CSS Animation

Comprehensive examples of the animation property and @keyframes — from basics to staggered sequences. MDN Reference

1. @keyframes basics

Define an animation with @keyframes and apply it via the animation shorthand: name duration timing-function delay iteration-count direction fill-mode.

Fade in

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

.box {
  animation: fade-in 1.8s ease infinite alternate;
}

Fade in + slide up

@keyframes fade-in-up {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.box {
  animation: fade-in-up 1.4s ease infinite alternate;
}

2. Transform animations

transform properties (translate, rotate, scale) compose well with @keyframes and are GPU-accelerated.

Bounce

@keyframes bounce {
  0%, 100% {
    transform: translateY(0);
    animation-timing-function: ease-in;
  }
  50% {
    transform: translateY(-48px);
    animation-timing-function: ease-out;
  }
}

.box {
  animation: bounce 1.2s infinite;
}

Spin (infinite)

@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.spinner {
  border-radius: 50%;
  border-top: 4px solid #fff;
  /* linear keeps rotation speed constant */
  animation: spin 1s linear infinite;
}

Scale pop

@keyframes scale-pop {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}

.box {
  animation: scale-pop 1.4s ease-in-out infinite;
}

Slide in from left

@keyframes slide-in-left {
  from {
    opacity: 0;
    transform: translateX(-64px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.box {
  animation: slide-in-left 1.2s ease infinite alternate;
}

3. Color & background animations

Color pulse

@keyframes pulse-color {
  0%   { background-color: #6366f1; }
  50%  { background-color: #ec4899; }
  100% { background-color: #6366f1; }
}

.box {
  animation: pulse-color 2s ease-in-out infinite;
}

4. animation-timing-function

Controls the acceleration curve between keyframes. Each track below uses the same 2 s duration — notice how the dot accelerates/decelerates differently.

ease

ease-in

ease-out

ease-in-out

linear

cubic-bezier(0.68, -0.55, 0.27, 1.55)

.ease         { animation-timing-function: ease; }
.ease-in      { animation-timing-function: ease-in; }
.ease-out     { animation-timing-function: ease-out; }
.ease-in-out  { animation-timing-function: ease-in-out; }
.linear       { animation-timing-function: linear; }

/* Custom cubic-bezier — overshoot effect */
.overshoot {
  animation-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
}

/* Can also be set per-keyframe: */
@keyframes bounce {
  0%   { animation-timing-function: ease-in; }
  50%  { animation-timing-function: ease-out; }
  100% { }
}

5. animation-fill-mode

Controls what styles apply before the animation starts (backwards) and after it ends (forwards). Use both to apply both rules.

ValueEffect
noneDefault. No styles applied outside animation.
forwardsRetains the last keyframe's styles after finishing.
backwardsApplies the first keyframe's styles during animation-delay.
bothCombines forwards and backwards.
/* Element stays visible after animating in once */
.enter {
  animation: fade-in-up 0.6s ease forwards;
}

/* Element is hidden during the delay, then animates */
.delayed-enter {
  animation: fade-in-up 0.6s ease 0.4s both;
}

/* Shorthand order:
   name | duration | timing | delay | count | direction | fill */
.full {
  animation: fade-in-up 0.6s ease 0.4s 1 normal both;
}

6. animation-direction

Controls whether iterations play forward, backward, or alternate.

normal
reverse
alternate
/* Plays 0% → 100% each iteration */
.normal    { animation-direction: normal; }

/* Plays 100% → 0% each iteration */
.reverse   { animation-direction: reverse; }

/* Alternates — odd: 0%→100%, even: 100%→0% */
.alternate { animation-direction: alternate; }

/* Same but starts in reverse */
.alt-rev   { animation-direction: alternate-reverse; }

7. animation-play-state

Toggle between running and paused to freeze an animation mid-frame. Hover the spinner below to pause it.

Hover to pause

.spinner {
  animation: spin 1.5s linear infinite;
}

/* Freeze mid-frame on hover */
.spinner:hover {
  animation-play-state: paused;
}

/* Or toggle via JavaScript */
el.style.animationPlayState = 'paused';
el.style.animationPlayState = 'running';

8. Multiple animations

Assign a comma-separated list to animation to run independent animations simultaneously on the same element.

Spin + color pulse

.box {
  /* spin and change colour independently */
  animation:
    spin        2s linear        infinite,
    pulse-color 3s ease-in-out   infinite;
}

/* Each property also accepts comma lists */
.box {
  animation-name:            spin, pulse-color;
  animation-duration:        2s,   3s;
  animation-timing-function: linear, ease-in-out;
  animation-iteration-count: infinite, infinite;
}

9. Staggered animations with CSS custom properties

Use a CSS custom property (--i) set inline to derive a per-element animation-delay without JavaScript.

Item 1Item 2Item 3Item 4Item 5Item 6
/* CSS */
.item {
  opacity: 0;
  animation: stagger-in 0.5s ease forwards;
  animation-delay: calc(var(--i) * 120ms);
}

@keyframes stagger-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* HTML — set --i per element */
<span class="item" style="--i: 0">Item 1</span>
<span class="item" style="--i: 1">Item 2</span>
<span class="item" style="--i: 2">Item 3</span>

/* Angular template */
@for (i of items; track i) {
  <span class="item" [style.--i]="i">Item {{ i + 1 }}</span>
}

10. Animation shorthand reference

Longhand propertyDefaultNotes
animation-namenoneName of the @keyframes rule.
animation-duration0sHow long one cycle takes.
animation-timing-functioneaseAcceleration curve.
animation-delay0sWait before starting. Negative values skip ahead.
animation-iteration-count1infinite or a number.
animation-directionnormalreverse / alternate / alternate-reverse.
animation-fill-modenoneforwards / backwards / both.
animation-play-staterunningpaused to freeze.
animation-timelineautoScroll-driven animations (scroll(), view()).
/* Full shorthand — order matters for duration vs delay (first <time> = duration) */
.box {
  animation: slide-in-left 0.6s ease-out 0.2s 1 normal both;
  /*         name          dur   timing  delay count dir   fill */
}

/* Multiple with named values for readability */
.box {
  animation:
    fade-in  0.4s ease     0s    1        normal forwards,
    slide-up 0.6s ease-out 0.1s  1        normal forwards;
}