Beginner12 min read

CSS Transitions: Smooth State Changes

12 min read
441 words
19 sections16 code blocks

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.

CSS
/* 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.

CSS
/* 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.

CSS
/* 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.

CSS
.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.

CSS
.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.

CSS
/* 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.

CSS
.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

CSS
/* 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

CSS
.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);
}
CSS
.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

CSS
.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%;
}
CSS
.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

CSS
.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s ease;
}

.accordion.open .accordion-content {
  max-height: 500px;
}

Fade In/Out

CSS
.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

CSS
.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.

CSS
/* 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.