Transitions & Animations
Why animate?
Animation makes interfaces feel alive. A button that smoothly changes color on hover feels more polished than one that snaps instantly. A notification that slides in draws attention naturally.
But animation should serve the user — it should provide feedback, guide attention, or make transitions feel natural. Don't animate just because you can.
CSS Transitions
Transitions animate the change between two states — they need a trigger (like
:hover).CSS
.button {
background: #2563eb;
color: white;
padding: 12px 24px;
border: none;
border-radius: 8px;
transition: background 0.2s ease, transform 0.2s ease;
}
.button:hover {
background: #1d4ed8;
transform: translateY(-2px);
}Transition properties
CSS
transition: property duration timing-function delay;- property — what to animate (
background,transform,opacity, orall). - duration — how long (
0.2s,300ms). - timing-function — the speed curve (
ease,linear,ease-in-out, or a customcubic-bezier()). - delay — wait before starting (
0sby default).
What to animate (and what not to)
Good to animate (GPU-accelerated, smooth):
transform(translate, scale, rotate)opacity
width,heighttop,left,right,bottommargin,padding
width, animate transform: scaleX(). Instead of top, use transform: translateY().CSS Animations with @keyframes
For animations that play on their own (not triggered by hover), use
@keyframes:CSS
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: fadeIn 0.4s ease-out;
}Multi-step animations
CSS
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.notification-dot {
animation: pulse 2s ease-in-out infinite;
}Animation properties
CSS
.element {
animation-name: fadeIn;
animation-duration: 0.4s;
animation-timing-function: ease-out;
animation-delay: 0s;
animation-iteration-count: 1; /* or 'infinite' */
animation-direction: normal; /* or 'reverse', 'alternate' */
animation-fill-mode: forwards; /* keep final state after animation ends */
}animation-fill-mode: forwards is important — without it, the element snaps back to its pre-animation state when the animation ends.Practical examples
Smooth page load
CSS
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.section {
animation: slideUp 0.5s ease-out both;
}
.section:nth-child(2) { animation-delay: 0.1s; }
.section:nth-child(3) { animation-delay: 0.2s; }Loading spinner
CSS
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
width: 24px;
height: 24px;
border: 3px solid #e2e8f0;
border-top-color: #2563eb;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}Smooth accordion
CSS
.accordion-content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
.accordion.open .accordion-content {
grid-template-rows: 1fr;
}
.accordion-inner {
overflow: hidden;
}This trick uses CSS Grid to animate height from 0 to auto — something that's impossible with
transition: height.Respecting user preferences
Some users experience motion sickness from animations. Always include a reduced-motion check:
CSS
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}Key takeaway
Use transitions for state changes (hover, focus, open/close) and
@keyframes for standalone animations. Stick to animating transform and opacity for smooth performance. Always respect prefers-reduced-motion. Less is often more — subtle animations feel more professional than flashy ones.