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.
| Value | Effect |
|---|---|
none | Default. No styles applied outside animation. |
forwards | Retains the last keyframe's styles after finishing. |
backwards | Applies the first keyframe's styles during animation-delay. |
both | Combines 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.
/* 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.
/* 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 property | Default | Notes |
|---|---|---|
animation-name | none | Name of the @keyframes rule. |
animation-duration | 0s | How long one cycle takes. |
animation-timing-function | ease | Acceleration curve. |
animation-delay | 0s | Wait before starting. Negative values skip ahead. |
animation-iteration-count | 1 | infinite or a number. |
animation-direction | normal | reverse / alternate / alternate-reverse. |
animation-fill-mode | none | forwards / backwards / both. |
animation-play-state | running | paused to freeze. |
animation-timeline | auto | Scroll-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;
}