18 minLesson 11 of 40
CSS3 & Responsive Design
Animations & Transitions
Animations & Transitions
CSS animations and transitions add motion to your UI — making it feel polished and responsive to user interaction. Done right, they guide attention and communicate state. Done wrong, they distract and annoy.
Transitions
Transitions animate a property change from one state to another:
/* transition: property duration timing-function delay */
.button {
background-color: #4a90e2;
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
/* Animate specific properties */
transition: background-color 200ms ease,
transform 200ms ease,
box-shadow 200ms ease;
/* Or all properties (less performant) */
transition: all 200ms ease;
}
.button:hover {
background-color: #2171c7;
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.2);
}
Timing Functions
.element {
/* Built-in easing curves */
transition: transform 300ms ease; /* slow start and end */
transition: transform 300ms ease-in; /* slow start */
transition: transform 300ms ease-out; /* slow end */
transition: transform 300ms ease-in-out; /* slow start and end (stronger) */
transition: transform 300ms linear; /* constant speed */
/* Custom cubic-bezier */
transition: transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1); /* spring */
transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1); /* Material Design */
/* Step functions */
transition: opacity 300ms steps(4); /* 4 discrete steps */
}
GPU-Accelerated Properties
For smooth 60fps animations, only animate these properties — they're handled by the GPU:
/* Fast (composite layer) */
transform: translateX() translateY() scale() rotate();
opacity: 0 to 1;
/* Slow (triggers layout or paint) */
width, height, top, left, margin, padding, background-color
/* The GPU trick for non-composited properties */
.animated-element {
will-change: transform; /* promote to GPU layer */
/* Use sparingly — each promotion costs memory */
}
Keyframe Animations
For multi-step or looping animations:
/* Define the animation */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
/* Apply the animation */
.card {
animation: fadeIn 400ms ease-out both;
/* animation-name: fadeIn
animation-duration: 400ms
animation-timing-function: ease-out
animation-fill-mode: both (apply first/last keyframe before/after)
*/
}
.spinner {
animation: spin 1s linear infinite;
}
.badge {
animation: pulse 2s ease-in-out infinite;
}
Staggered Animations
/* Animate list items one by one */
.list-item {
animation: fadeIn 400ms ease-out both;
}
.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 50ms; }
.list-item:nth-child(3) { animation-delay: 100ms; }
.list-item:nth-child(4) { animation-delay: 150ms; }
.list-item:nth-child(5) { animation-delay: 200ms; }
/* Or with CSS custom properties + JavaScript */
.list-item {
animation: fadeIn 400ms ease-out both;
animation-delay: calc(var(--index) * 50ms);
}
document.querySelectorAll(".list-item").forEach((el, i) => {
el.style.setProperty("--index", i);
});
Loading Skeleton
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
}
.skeleton-text { height: 1rem; margin-bottom: 0.5rem; }
.skeleton-title { height: 1.5rem; width: 60%; margin-bottom: 1rem; }
.skeleton-image { height: 200px; width: 100%; }
Page Transitions
/* Fade in on page load */
body {
animation: fadeIn 300ms ease-out;
}
/* Route transitions (React Router / Next.js) */
.page-enter {
opacity: 0;
transform: translateY(8px);
}
.page-enter-active {
opacity: 1;
transform: translateY(0);
transition: opacity 300ms, transform 300ms;
}
.page-exit { opacity: 1; }
.page-exit-active {
opacity: 0;
transition: opacity 200ms;
}
Respecting User Preferences
Always disable animations for users who prefer reduced motion:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Or opt-in approach */
@media (prefers-reduced-motion: no-preference) {
.card {
transition: transform 200ms ease;
}
}
Next lesson: DOM Manipulation — using JavaScript to interact with HTML elements.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises