A
AiTechWorlds
AiTechWorlds
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 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);
}.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 */
}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 */
}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;
}/* 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);
});.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%; }/* 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;
}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 this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises