CSS Animations & Transitions Guide
A complete reference for CSS motion. Master transitions for smooth state changes and animations for complex sequences. Includes timing functions, common patterns, and performance best practices.
Transitions
Smooth changes between CSS property values over a duration.
transition (Shorthand)
/* transition: property duration timing-function delay */
.element {
transition: all 0.3s ease 0s;
transition: opacity 0.3s ease;
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Multiple properties */
.element {
transition:
opacity 0.3s ease,
transform 0.3s ease,
background-color 0.3s ease;
}
/* All properties */
.element {
transition: all 0.3s ease;
}
Individual Properties
.element {
/* What to transition */
transition-property: opacity;
transition-property: transform;
transition-property: all;
/* How long */
transition-duration: 0.3s;
transition-duration: 300ms;
/* Timing curve */
transition-timing-function: ease;
transition-timing-function: linear;
transition-timing-function: ease-in-out;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
/* Delay before starting */
transition-delay: 0s;
transition-delay: 0.2s;
}
Practical Examples
/* Button hover */
.button {
background: blue;
transform: scale(1);
transition: all 0.2s ease;
}
.button:hover {
background: darkblue;
transform: scale(1.05);
}
/* Fade in/out */
.modal {
opacity: 0;
transition: opacity 0.3s ease;
}
.modal.is-open {
opacity: 1;
}
/* Smooth color change */
.link {
color: blue;
transition: color 0.2s ease;
}
.link:hover {
color: purple;
}
Animations
Complex, multi-step animations using @keyframes.
animation (Shorthand)
/* animation: name duration timing-function delay iteration-count direction fill-mode play-state */
.element {
animation: slideIn 0.5s ease 0s 1 normal forwards running;
/* Common patterns */
animation: fadeIn 0.3s ease;
animation: bounce 1s ease-in-out infinite;
animation: spin 2s linear infinite;
}
Individual Properties
.element {
/* Name of @keyframes */
animation-name: slideIn;
/* Duration */
animation-duration: 1s;
/* Timing function */
animation-timing-function: ease-in-out;
/* Delay before start */
animation-delay: 0.5s;
/* How many times */
animation-iteration-count: 1;
animation-iteration-count: infinite;
animation-iteration-count: 3;
/* Direction */
animation-direction: normal;
animation-direction: reverse;
animation-direction: alternate;
animation-direction: alternate-reverse;
/* What happens before/after */
animation-fill-mode: none;
animation-fill-mode: forwards; /* stays at end */
animation-fill-mode: backwards; /* starts at beginning */
animation-fill-mode: both;
/* Play or pause */
animation-play-state: running;
animation-play-state: paused;
}
Multiple Animations
.element {
animation:
fadeIn 0.5s ease,
slideUp 0.5s ease,
bounce 1s ease-in-out 0.5s;
}
/* Or with individual properties */
.element {
animation-name: fadeIn, slideUp;
animation-duration: 0.5s, 0.8s;
animation-timing-function: ease, ease-out;
}
@keyframes
Define animation sequences with multiple steps.
Basic Syntax
/* From/To syntax */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Percentage syntax */
@keyframes slideIn {
0% {
transform: translateX(-100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
/* Multiple steps */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-30px);
}
}
/* Complex animation */
@keyframes complexMove {
0% {
transform: translateX(0) rotate(0deg);
opacity: 0;
}
50% {
transform: translateX(100px) rotate(180deg);
opacity: 1;
}
100% {
transform: translateX(200px) rotate(360deg);
opacity: 0;
}
}
Common Keyframe Patterns
/* Fade In */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Slide In from Right */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Scale In */
@keyframes scaleIn {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* Rotate In */
@keyframes rotateIn {
from {
transform: rotate(-200deg) scale(0);
opacity: 0;
}
to {
transform: rotate(0) scale(1);
opacity: 1;
}
}
/* Shake */
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
20%, 40%, 60%, 80% { transform: translateX(10px); }
}
/* Wiggle */
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-5deg); }
75% { transform: rotate(5deg); }
}
/* Heartbeat */
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
25% { transform: scale(1.1); }
50% { transform: scale(1); }
}
/* Flip */
@keyframes flip {
0% { transform: perspective(400px) rotateY(0); }
100% { transform: perspective(400px) rotateY(360deg); }
}
Timing Functions
Control the acceleration curve of animations and transitions.
Built-in Timing Functions
/* Linear - constant speed */
.element { transition-timing-function: linear; }
/* Ease (default) - slow start, fast middle, slow end */
.element { transition-timing-function: ease; }
/* Ease-in - slow start, fast end */
.element { transition-timing-function: ease-in; }
/* Ease-out - fast start, slow end */
.element { transition-timing-function: ease-out; }
/* Ease-in-out - slow start and end */
.element { transition-timing-function: ease-in-out; }
/* Steps - no smoothing */
.element {
transition-timing-function: steps(4);
transition-timing-function: steps(10, end);
transition-timing-function: step-start;
transition-timing-function: step-end;
}
Interactive Comparison
Hover over each to see the timing difference:
Custom Cubic Bezier
/* cubic-bezier(x1, y1, x2, y2) */
.element {
/* Material Design */
transition: cubic-bezier(0.4, 0, 0.2, 1);
/* Ease-in-out (equivalent) */
transition: cubic-bezier(0.42, 0, 0.58, 1);
/* Custom bounce */
transition: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* Anticipate */
transition: cubic-bezier(0.36, 0, 0.66, -0.56);
/* Overshoot */
transition: cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Common presets */
.smooth { transition: cubic-bezier(0.4, 0, 0.2, 1) 0.3s; }
.snappy { transition: cubic-bezier(0.4, 0, 1, 1) 0.2s; }
.bouncy { transition: cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.5s; }
Tip: Use Cubic Bezier Studio to create and test custom timing functions visually.
Common Patterns
Ready-to-use animation patterns for typical use cases.
Fade Animations
/* Fade In */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.3s ease;
}
/* Fade Out */
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.fade-out {
animation: fadeOut 0.3s ease forwards;
}
/* Fade In Up */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.5s ease;
}
Slide Animations
/* Slide In from Left */
@keyframes slideInLeft {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
/* Slide In from Right */
@keyframes slideInRight {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
/* Slide In from Top */
@keyframes slideInDown {
from { transform: translateY(-100%); }
to { transform: translateY(0); }
}
/* Slide In from Bottom */
@keyframes slideInUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
Scale Animations
/* Scale In */
@keyframes scaleIn {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* Scale In Center */
@keyframes scaleInCenter {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
/* Pop In (with bounce) */
@keyframes popIn {
0% {
transform: scale(0);
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;
}
}
Rotate Animations
/* Spin */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
animation: spin 1s linear infinite;
}
/* Rotate In */
@keyframes rotateIn {
from {
transform: rotate(-180deg);
opacity: 0;
}
to {
transform: rotate(0);
opacity: 1;
}
}
/* Flip */
@keyframes flip {
0% { transform: perspective(400px) rotateY(0); }
100% { transform: perspective(400px) rotateY(360deg); }
}
Bounce Animations
/* Bounce */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-30px);
}
}
/* Bounce In */
@keyframes bounceIn {
0% {
transform: scale(0.3);
opacity: 0;
}
50% {
transform: scale(1.05);
}
70% {
transform: scale(0.9);
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* Bounce (realistic) */
@keyframes bounceRealistic {
0%, 100% { transform: translateY(0); }
10%, 30% { transform: translateY(0); }
20% { transform: translateY(-20px); }
40% { transform: translateY(-10px); }
50%, 55% { transform: translateY(0); }
}
Attention Seekers
/* Shake */
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
20%, 40%, 60%, 80% { transform: translateX(10px); }
}
/* Pulse */
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.05);
opacity: 0.8;
}
}
/* Wobble */
@keyframes wobble {
0%, 100% { transform: translateX(0) rotate(0deg); }
15% { transform: translateX(-25px) rotate(-5deg); }
30% { transform: translateX(20px) rotate(3deg); }
45% { transform: translateX(-15px) rotate(-3deg); }
60% { transform: translateX(10px) rotate(2deg); }
75% { transform: translateX(-5px) rotate(-1deg); }
}
/* Tada */
@keyframes tada {
0%, 100% { transform: scale(1) rotate(0deg); }
10%, 20% { transform: scale(0.9) rotate(-3deg); }
30%, 50%, 70%, 90% { transform: scale(1.1) rotate(3deg); }
40%, 60%, 80% { transform: scale(1.1) rotate(-3deg); }
}
Performance Tips
Optimize animations for smooth 60fps performance.
GPU-Accelerated Properties
transform
opacity
filter
These properties can be animated smoothly at 60fps because they don't trigger layout or paint.
width
, height
top
, left
margin
, padding
These trigger layout recalculation and paint, causing jank.
/* ❌ Bad - animates layout properties */
.element {
transition: width 0.3s, height 0.3s, left 0.3s;
}
/* ✅ Good - animates GPU properties */
.element {
transition: transform 0.3s, opacity 0.3s;
}
/* ❌ Bad - moving with position */
.slide-bad {
left: 0;
transition: left 0.3s;
}
.slide-bad:hover {
left: 100px;
}
/* ✅ Good - moving with transform */
.slide-good {
transform: translateX(0);
transition: transform 0.3s;
}
.slide-good:hover {
transform: translateX(100px);
}
will-change Property
/* Hint browser about upcoming changes */
.element {
will-change: transform;
}
.modal {
will-change: opacity, transform;
}
/* Only use when needed, remove after */
.element {
will-change: transform;
}
.element:hover {
transform: scale(1.2);
}
.element:not(:hover) {
will-change: auto; /* reset after animation */
}
/* ❌ Don't overuse */
* {
will-change: transform; /* Too many elements */
}
/* ❌ Don't use for non-changing properties */
.element {
will-change: color; /* Not being animated */
}
Important: Use will-change
sparingly. It creates a new layer and uses more memory. Only apply it to elements that actually animate.
Compositing Hints
/* Force layer creation (use carefully) */
.element {
transform: translateZ(0);
/* or */
transform: translate3d(0, 0, 0);
/* or */
backface-visibility: hidden;
}
/* Prefer transform over position */
/* ❌ Triggers layout */
@keyframes moveLeft {
from { left: 0; }
to { left: 100px; }
}
/* ✅ Uses compositor */
@keyframes moveLeft {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
Best Practices
/* 1. Reduce animation duration for perceived performance */
.element {
transition: 0.2s ease; /* Faster feels snappier */
}
/* 2. Use appropriate timing functions */
.button {
/* Ease-out feels more responsive on interactions */
transition: transform 0.2s ease-out;
}
/* 3. Debounce expensive animations */
.element {
/* Only run on user interaction end */
transition: transform 0.3s ease;
transition-delay: 0.1s;
}
/* 4. Reduce motion for accessibility */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* 5. Use contain for complex animations */
.animated-container {
contain: layout style paint;
}
Advanced Techniques
Animation Delays for Sequencing
/* Stagger animations */
.item:nth-child(1) { animation-delay: 0s; }
.item:nth-child(2) { animation-delay: 0.1s; }
.item:nth-child(3) { animation-delay: 0.2s; }
.item:nth-child(4) { animation-delay: 0.3s; }
/* Using calc */
.item {
animation: fadeIn 0.5s ease;
animation-delay: calc(var(--index) * 0.1s);
}
/* CSS counter approach */
@for $i from 1 through 10 {
.item:nth-child(#{$i}) {
animation-delay: #{$i * 0.1}s;
}
}
Pausing Animations
/* Control with play-state */
.animation {
animation: spin 2s linear infinite;
animation-play-state: running;
}
.animation:hover {
animation-play-state: paused;
}
/* Toggle with JavaScript */
.animation.paused {
animation-play-state: paused;
}
Animation Fill Modes
/* How animation affects element before/after */
/* none - no styles applied outside animation */
.element {
animation: fadeIn 1s ease;
animation-fill-mode: none;
}
/* forwards - keeps final keyframe styles */
.element {
animation: fadeIn 1s ease;
animation-fill-mode: forwards;
}
/* backwards - applies first keyframe during delay */
.element {
animation: fadeIn 1s ease 2s;
animation-fill-mode: backwards;
}
/* both - applies both forwards and backwards */
.element {
animation: fadeIn 1s ease 2s;
animation-fill-mode: both;
}
Chaining Animations
/* Sequential animations using delays */
.element {
animation:
fadeIn 0.3s ease 0s,
slideUp 0.3s ease 0.3s,
bounce 0.5s ease 0.6s;
animation-fill-mode: both;
}
/* Or use a combined keyframe */
@keyframes fadeSlideIn {
0% {
opacity: 0;
transform: translateY(20px);
}
50% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 1;
transform: translateY(0) scale(1.05);
}
}
CSS Variables in Animations
/* Dynamic animations with custom properties */
.element {
--rotation: 180deg;
--scale: 1.5;
--duration: 1s;
animation: rotate var(--duration) ease;
}
@keyframes rotate {
from {
transform: rotate(0deg) scale(1);
}
to {
transform: rotate(var(--rotation)) scale(var(--scale));
}
}
/* Change with JavaScript */
/* element.style.setProperty('--rotation', '360deg'); */
Responsive Animations
/* Adjust for viewport size */
.element {
animation: slideIn 0.5s ease;
}
@media (max-width: 768px) {
.element {
animation-duration: 0.3s; /* Faster on mobile */
}
}
/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
.element {
animation: none;
transition: none;
}
}
/* Alternative: instant animations */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}