CSS transitions are the simplest way to add motion to your interfaces. They animate the change between two states — from the current value of a property to a new value — whenever that property changes.
The Transition Property
A transition needs four pieces of information:
.button {
transition: background-color 0.2s ease-out 0s;
/* property duration timing delay */
}- property — which CSS property to animate
- duration — how long the transition takes
- timing-function — the acceleration curve
- delay — time before the transition starts (optional, defaults to 0)
You can transition multiple properties:
.card {
transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}Or use all to transition every changing property (use with caution — it can animate properties you did not intend):
.card {
transition: all 0.2s ease-out;
}Hover States
The most common use of transitions is smooth hover effects:
Without the transition, the card would jump to its hover state instantly. With it, the card lifts smoothly upward and its shadow deepens, creating a sense of depth.
Focus States
Transitions also enhance keyboard navigation by making focus indicators feel polished:
The smooth transition from gray border to blue border with a glowing ring makes focus visible and pleasant.
Timing Functions Deep Dive
Timing functions control the speed curve of a transition. CSS provides built-in keywords and the cubic-bezier() function for custom curves:
Each keyword maps to a specific cubic-bezier() curve:
| Keyword | cubic-bezier | Character |
|---|---|---|
ease | (0.25, 0.1, 0.25, 1) | General purpose |
linear | (0, 0, 1, 1) | Mechanical, robotic |
ease-in | (0.42, 0, 1, 1) | Accelerating — good for exits |
ease-out | (0, 0, 0.58, 1) | Decelerating — good for entrances |
ease-in-out | (0.42, 0, 0.58, 1) | Smooth — good for position changes |
Use tools like cubic-bezier.com to experiment with custom curves visually.
Transition Performance
Not all properties are equal when it comes to performance. Stick to properties that can be hardware-accelerated:
If you need to animate size changes, use transform: scale() instead of width/height. If you need to animate position, use transform: translate() instead of top/left.
Common Patterns
Button Press Effect
Fade-In on State Change
Using visibility alongside opacity ensures the hidden tooltip does not block clicks on elements behind it.
Color Shift Link
Expanding Border
Transition Gotchas
- You cannot transition
display— useopacityandvisibilityinstead, or use the newertransition-behavior: allow-discrete(limited support). - You cannot transition
autovalues —height: autocannot be transitioned. Usemax-heightwith a large value as a workaround, or use CSS Grid'sgrid-template-rows: 0frto1frtrick. - Initial page load — transitions can fire on page load if an element starts in a different state. Add a
no-transitionclass that you remove after load if this is a problem.
This is the cleanest way to animate an element from zero height to its natural content height.