CSS Transitions: Smooth State Changes
What Are CSS Transitions?
CSS transitions let you smoothly animate changes between two states of an element. Instead of a property snapping instantly from one value to another, a transition creates a gradual change over a specified duration.
Without a transition, hovering over a button changes its color instantly. With a transition, the color fades smoothly.
The transition Property
The `transition` shorthand combines four individual properties: property, duration, timing function, and delay.
/* Shorthand: property duration timing-function delay */
.button {
background-color: #3498db;
color: white;
padding: 12px 24px;
border: none;
border-radius: 6px;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #2980b9;
}Transition Property
The `transition-property` specifies which CSS property to animate.
/* Single property */
.box {
transition-property: background-color;
}
/* Multiple properties */
.card {
transition-property: transform, box-shadow, background-color;
}
/* All animatable properties */
.element {
transition-property: all;
}Performance tip: Avoid using `all` in production. Instead, list the specific properties you want to animate. Transitioning `all` can cause unexpected animations and performance issues.
/* Bad: transitions everything, including layout properties */
.card {
transition: all 0.3s ease;
}
/* Good: only transition what you need */
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}Transition Duration
The `transition-duration` sets how long the transition takes.
.fast { transition-duration: 0.15s; } /* Quick micro-interaction */
.normal { transition-duration: 0.3s; } /* Standard UI transition */
.slow { transition-duration: 0.5s; } /* Deliberate, noticeable */
.very-slow { transition-duration: 1s; } /* Dramatic effect */Guidelines for duration:
- Hover effects: 0.15s to 0.3s feels responsive
- Color changes: 0.2s to 0.3s
- Size or position changes: 0.3s to 0.5s
- Page-level transitions: 0.3s to 0.6s
- Anything over 1s feels sluggish for UI interactions
Timing Functions
The `transition-timing-function` controls the speed curve of the transition. This determines how the animation accelerates and decelerates.
.ease { transition-timing-function: ease; }
/* Default. Slow start, fast middle, slow end */
.linear { transition-timing-function: linear; }
/* Constant speed throughout */
.ease-in { transition-timing-function: ease-in; }
/* Starts slow, ends fast */
.ease-out { transition-timing-function: ease-out; }
/* Starts fast, ends slow - feels most natural for UI */
.ease-in-out { transition-timing-function: ease-in-out; }
/* Slow start and end, fast middle */Custom Cubic Bezier
For precise control, use `cubic-bezier()` to define your own timing curve.
/* Snappy feel */
.snappy {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
/* Bouncy overshoot */
.bouncy {
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
/* Quick start, gentle end */
.quick-out {
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}Best practice: Use `ease-out` for elements entering the screen, `ease-in` for elements leaving, and `ease-in-out` for elements changing state in place.
Transition Delay
The `transition-delay` sets a waiting period before the transition starts.
.delayed {
transition: transform 0.3s ease 0.1s; /* 0.1s delay */
}
/* Staggered effect on multiple items */
.item:nth-child(1) { transition-delay: 0s; }
.item:nth-child(2) { transition-delay: 0.05s; }
.item:nth-child(3) { transition-delay: 0.1s; }
.item:nth-child(4) { transition-delay: 0.15s; }Transitioning Multiple Properties
/* Same duration and timing for all */
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
/* Different durations */
.element {
transition: background-color 0.2s ease, transform 0.4s ease-out, opacity 0.3s ease;
}
/* Shorthand with multiple */
.button {
background-color: #3498db;
color: white;
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition:
background-color 0.2s ease,
transform 0.3s ease,
box-shadow 0.3s ease;
}
.button:hover {
background-color: #2980b9;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}Practical Transition Examples
Hover Card with Lift Effect
.card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
}Animated Underline Link
.link {
position: relative;
text-decoration: none;
color: #333;
}
.link::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background-color: #3498db;
transform: scaleX(0);
transform-origin: right;
transition: transform 0.3s ease;
}
.link:hover::after {
transform: scaleX(1);
transform-origin: left;
}Color-Changing Button
.btn-gradient {
padding: 12px 32px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
background-size: 200% 200%;
background-position: 0% 50%;
transition: background-position 0.5s ease;
}
.btn-gradient:hover {
background-position: 100% 50%;
}Expanding Search Bar
.search-input {
width: 200px;
padding: 10px 16px;
border: 2px solid #ddd;
border-radius: 50px;
outline: none;
transition: width 0.4s ease, border-color 0.3s ease;
}
.search-input:focus {
width: 350px;
border-color: #3498db;
}Smooth Accordion
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease;
}
.accordion.open .accordion-content {
max-height: 500px;
}Fade In/Out
.fade-element {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.fade-element.visible {
opacity: 1;
visibility: visible;
}Image Overlay on Hover
.image-card {
position: relative;
overflow: hidden;
border-radius: 12px;
}
.image-card img {
width: 100%;
display: block;
transition: transform 0.5s ease;
}
.image-card .overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.image-card:hover img {
transform: scale(1.1);
}
.image-card:hover .overlay {
opacity: 1;
}Which Properties Can Be Transitioned?
Not all CSS properties can be transitioned. Properties that can be transitioned have a clear start and end value that can be interpolated. Here are the most commonly transitioned properties:
- Colors: color, background-color, border-color
- Opacity: opacity
- Transforms: transform (translate, rotate, scale, skew)
- Size: width, height, max-width, max-height, padding, margin
- Position: top, right, bottom, left
- Borders: border-width, border-radius
- Shadows: box-shadow, text-shadow
- Typography: font-size, letter-spacing
Properties that cannot be transitioned include `display`, `font-family`, and `position`.
Accessibility: Reduced Motion
Some users experience discomfort or nausea from animations. Always respect the `prefers-reduced-motion` media query.
/* Disable transitions for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition-duration: 0.01ms;
transition-delay: 0ms;
}
}This is essential for accessibility and should be included in every project.
Summary
CSS transitions bring your interfaces to life with smooth, polished state changes. Set the property to animate, the duration for speed, a timing function for feel, and an optional delay. Use `ease-out` for natural-feeling UI interactions, keep durations under 0.5s for responsiveness, and always include a `prefers-reduced-motion` fallback for accessibility. For best performance, transition only `transform` and `opacity` whenever possible.
In the next article, you will learn about CSS keyframe animations for more complex motion.