CSS Transitions
A CSS transition animates the change between two states of an element. When a property changes — on hover, on focus, on class change — instead of snapping instantly to the new value, it glides smoothly over a set duration. Transitions make interfaces feel alive and responsive to user actions.
How Transitions Work
DIAGRAM — Without and with transition:
Without transition:
State A → [instant jump] → State B
With transition:
State A → [smooth glide over 0.3s] → State B
/* State A — normal button */
.btn {
background: blue;
transition: background 0.3s ease;
}
/* State B — hovered button */
.btn:hover {
background: darkblue;
/* The change from blue to darkblue takes 0.3s */
}
The transition Shorthand
transition: property duration timing-function delay;
| Part | Required | Description |
|---|---|---|
| property | Yes | Which CSS property to animate (or all) |
| duration | Yes | How long the transition takes (e.g., 0.3s, 200ms) |
| timing-function | No (default: ease) | The speed curve of the transition |
| delay | No (default: 0) | Wait before starting |
Basic Examples
/* Transition one property */
.link {
color: blue;
transition: color 0.2s;
}
.link:hover { color: red; }
/* Transition multiple properties */
.card {
background: white;
box-shadow: none;
transition: background 0.3s, box-shadow 0.3s;
}
.card:hover {
background: #f0f8ff;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}
/* Transition everything */
.btn {
transition: all 0.25s ease;
}
Avoid transition: all on complex elements. It watches every property for changes, which can cause unexpected transitions and hurt performance. List only the properties you want animated.
Timing Functions — The Speed Curve
The timing function controls the acceleration and deceleration of the transition — how it "feels" in motion.
DIAGRAM — Timing functions:
ease (default):
slow → fast → slow ← smooth, natural feeling
────────────────────────→
linear:
constant speed ← mechanical, robotic feeling
────────────────────────→
ease-in:
slow → fast ← starts sluggish, accelerates
────────────────────────→
ease-out:
fast → slow ← starts fast, decelerates (most natural for UI)
────────────────────────→
ease-in-out:
slow → fast → slow ← symmetric, smooth both ends
────────────────────────→
.card {
transition: transform 0.4s ease-out;
}
.card:hover {
transform: translateY(-4px);
}
Custom Timing with cubic-bezier()
You can define any curve using four numbers that control two control points of a bezier curve.
/* Bouncy overshoot effect */
.btn {
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Snappy, sharp deceleration */
.menu {
transition: opacity 0.2s cubic-bezier(0.0, 0.0, 0.2, 1);
}
Use tools like easings.net or the Chrome DevTools cubic-bezier editor to visually design timing curves.
Transition Delay
The delay waits before starting the transition. Useful for staggering multiple elements.
.nav-item:nth-child(1) { transition: opacity 0.3s 0s; }
.nav-item:nth-child(2) { transition: opacity 0.3s 0.05s; }
.nav-item:nth-child(3) { transition: opacity 0.3s 0.10s; }
.nav-item:nth-child(4) { transition: opacity 0.3s 0.15s; }
DIAGRAM — Staggered delay:
Item 1: starts at 0s → fades in first
Item 2: starts at 0.05s → fades in 50ms later
Item 3: starts at 0.10s → fades in 100ms later
Item 4: starts at 0.15s → fades in 150ms later
Creates a cascading entrance effect.
Transitioning Multiple Properties
.card {
transition:
transform 0.3s ease-out,
box-shadow 0.3s ease-out,
background-color 0.2s ease;
}
.card:hover {
transform: translateY(-6px);
box-shadow: 0 12px 30px rgba(0,0,0,0.15);
background-color: #f9f9f9;
}
What Can Be Transitioned?
Only properties with numeric or color values can be transitioned — the browser needs two values it can calculate between.
DIAGRAM — Transitionable vs non-transitionable:
✅ CAN transition:
color, background-color (color → color)
opacity (0 → 1)
width, height, padding, margin
transform (scale, translate, rotate)
box-shadow, border-color, border-radius
font-size, line-height
top, left, right, bottom
❌ CANNOT transition:
display (block/none — no numeric middle ground)
visibility → use opacity instead
font-family, content
Common UI Transition Patterns
Button Press Effect
.btn {
transform: scale(1);
transition: transform 0.1s ease-in;
}
.btn:active {
transform: scale(0.96);
}
Card Lift on Hover
.card {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: translateY(0);
transition: box-shadow 0.3s, transform 0.3s;
}
.card:hover {
box-shadow: 0 12px 24px rgba(0,0,0,0.2);
transform: translateY(-4px);
}
Fade In on Class Toggle
.modal {
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.modal.visible {
opacity: 1;
pointer-events: auto;
}
Underline Grow on Hover
.nav-link {
position: relative;
}
.nav-link::after {
content: "";
position: absolute;
bottom: -2px;
left: 0;
width: 0%;
height: 2px;
background: currentColor;
transition: width 0.3s ease;
}
.nav-link:hover::after {
width: 100%;
}
Performance — Which Properties to Transition
Browsers animate some properties in the GPU compositor thread — smooth even on slow devices. Others force the browser to recalculate the layout on every frame — expensive.
DIAGRAM — GPU-accelerated vs layout-triggering:
✅ GPU-safe (always prefer these):
transform: translate, scale, rotate
opacity
⚠ Layout-triggering (use carefully):
width, height, top, left, margin, padding
These cause layout recalculations every frame.
Rule: If you can achieve an effect with transform or opacity,
choose them over geometric properties.
Respecting Reduced Motion
@media (prefers-reduced-motion: reduce) {
* {
transition-duration: 0.01ms !important;
}
}
This cuts all transitions to near-instant for users who have enabled reduced-motion in their OS settings — an important accessibility consideration.
Transitions are the most low-effort, high-impact CSS technique for making a site feel professional. A 0.2s ease-out on a button or link hover is often the single change that makes a page feel polished instead of flat.
