Episode 1 — Fundamentals / 1.12 — CSS Animations and Motion Design
1.12.a — CSS Transitions
In one sentence: A transition smoothly interpolates a CSS property from one value to another over a specified duration — the simplest way to add motion to your UI.
Navigation: ← 1.12 Overview · 1.12.b — CSS Transform →
1. The transition shorthand
/* transition: property duration timing-function delay; */
transition: opacity 300ms ease-in-out 0s;
| Component | What it controls | Default |
|---|---|---|
| property | Which CSS property to animate (all = every animatable prop) | all |
| duration | How long the change takes | 0s (instant) |
| timing-function | Acceleration curve | ease |
| delay | Wait before the transition starts | 0s |
Longhand equivalents
transition-property: opacity;
transition-duration: 300ms;
transition-timing-function: ease-in-out;
transition-delay: 0s;
2. Which properties can be transitioned?
A property is animatable if the browser can compute intermediate values.
| Animatable (common) | NOT animatable |
|---|---|
opacity, color, background-color | display (no intermediate between none / block) |
width, height, margin, padding | font-family (can't blend fonts) |
transform, box-shadow, border-radius | position (static / relative — keyword, not numeric) |
top, left, right, bottom | visibility* (special — can only snap at 0 / end) |
Rule of thumb: if a value is numeric or color-based, it's probably animatable.
3. Timing functions explained
ease ──╮ ╭── fast start, slow end (default)
╰───╯
linear ────────── constant speed
ease-in ──╮ slow start, fast end
╰──────
ease-out ──────╮ fast start, slow end
╰──
ease-in-out ──╮ ╭── slow-fast-slow (symmetric S-curve)
╰╯
cubic-bezier() — custom curves
Every keyword maps to a cubic-bezier:
ease → cubic-bezier(0.25, 0.1, 0.25, 1.0)
linear → cubic-bezier(0.0, 0.0, 1.0, 1.0)
ease-in → cubic-bezier(0.42, 0.0, 1.0, 1.0)
ease-out → cubic-bezier(0.0, 0.0, 0.58, 1.0)
ease-in-out → cubic-bezier(0.42, 0.0, 0.58, 1.0)
You can create overshoot / bounce effects by pushing control points beyond 0–1:
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
4. Duration psychology
| Duration | User perception | Use case |
|---|---|---|
| 100–200 ms | Feels instant | Hover color changes, small icon tweaks |
| 200–300 ms | Feels responsive | Button state changes, tooltips |
| 300–500 ms | Feels smooth | Panel slides, modal entrances |
| > 500 ms | Feels slow or cinematic | Use sparingly — page-level transitions, dramatic reveals |
Interview tip: "I default to 200–300 ms for micro-interactions and reserve anything longer for deliberate choreography."
5. Multiple transitions
Comma-separate each property:
.card {
transition: transform 300ms ease-out,
box-shadow 300ms ease-out,
opacity 200ms linear;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
opacity: 0.95;
}
Using
transition: all 300msis convenient but can cause unintended animations on properties you didn't plan for (e.g.,background-colorchanging on theme switch). Prefer listing properties explicitly.
6. Transition events in JavaScript
element.addEventListener('transitionend', (e) => {
console.log(`${e.propertyName} finished in ${e.elapsedTime}s`);
});
| Event | When it fires |
|---|---|
transitionrun | Transition is created (including during delay) |
transitionstart | Delay has elapsed; interpolation begins |
transitionend | Interpolation finishes |
transitioncancel | Transition is cancelled mid-way |
7. Common patterns
Hover lift
.btn {
transition: transform 200ms ease-out, box-shadow 200ms ease-out;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
Color change
.link {
color: #333;
transition: color 150ms ease;
}
.link:hover {
color: #0066ff;
}
Size change (with caution)
.avatar {
width: 48px;
height: 48px;
transition: width 300ms ease, height 300ms ease;
}
.avatar:hover {
width: 64px;
height: 64px;
}
Performance note: animating
width/heighttriggers layout every frame. Prefertransform: scale()for the same visual effect at a fraction of the cost (see 1.12.e).
8. Accessibility: prefers-reduced-motion
Some users experience motion sickness, vestibular disorders, or simply prefer less movement. Respect their OS setting:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
Why
0.01msinstead of0s? A zero duration can breaktransitionendevent listeners. A near-zero value still fires the event but removes visible motion.
9. Key takeaways
transitionsmoothly animates one property change from A → B.- List properties explicitly rather than relying on
all. - Keep micro-interactions in the 200–300 ms range.
- Use
cubic-bezier()for custom curves or playful overshoot. - Always include a
prefers-reduced-motionfallback.
Explain-It Challenge
Explain without notes:
- Why
transition: all 500mscan cause unexpected behavior when a theme class is toggled. - How
transitionendfires once per animated property — and what that means if you transition three properties simultaneously. - Why
0.01msis preferable to0sin aprefers-reduced-motionreset.
Navigation: ← 1.12 Overview · 1.12.b — CSS Transform →