Complete CSS Course Module 14: Advanced: Transitions, Transforms and Animations

CSS Transitions: Smooth Hover Effects

  • Beginner
  • Lesson 14.1
  • 4 min read
  • 60 of 77 in the course

In short

CSS transitions make a style change happen smoothly over time instead of instantly. They are the easiest way to add polished hover effects to buttons and cards. What is a CSS transition? Normally, when a style changes, for example on :hover, the change is instant.

CSS transitions make a style change happen smoothly over time instead of instantly. They are the easiest way to add polished hover effects to buttons and cards.

What is a CSS transition?

Normally, when a style changes, for example on :hover, the change is instant. The button jumps from blue to dark blue in zero seconds. A transition tells the browser to slide between the old value and the new value over a short time.

Think of a light switch versus a dimmer. Without a transition, the light snaps on. With a transition, it fades up.

A transition has four parts:

PropertyWhat it controlsExample
transition-propertyWhich property animatesbackground-color
transition-durationHow long it takes0.3s or 300ms
transition-timing-functionThe speed curveease, linear, ease-in-out
transition-delayWait time before it starts0.1s

Most of the time you use the transition shorthand, in this order: property, duration, timing function, delay.

.link {
  color: #4f46e5;
  transition: color 0.3s ease;
}

.link:hover {
  color: #db2777;
}

When you hover the link, its color fades from purple to pink over 0.3 seconds. When you move the mouse away, it fades back.

Common mistake: Putting transition only on the :hover rule. Then the effect is smooth on the way in but snaps back instantly on the way out. Put the transition on the normal state, so it works in both directions.

Timing functions and multiple transitions

The timing function controls the feel of the movement:

  • ease (the default): starts slowly, speeds up, slows down at the end. Feels natural.
  • linear: the same speed the whole time. Good for colors, but can look robotic for movement.
  • ease-out: starts fast and slows down. Great for things appearing.
  • ease-in: starts slowly and ends fast. Good for things leaving.
  • cubic-bezier(...): your own custom curve. DevTools has an editor to help you draw one.

You can transition several properties at once with commas, each with its own timing:

.box {
  transition: background-color 0.2s ease, transform 0.3s ease-out 0.05s;
}

Here the background changes in 0.2 seconds, and the movement takes 0.3 seconds, starting 0.05 seconds later.

Tip: Avoid transition: all. It animates every property that changes, including ones you did not expect, and it can hurt performance. List the exact properties you want.

Which CSS properties animate well?

Only properties with values "in between" can transition. A color can be halfway between blue and red. A width can be halfway between 100px and 200px. But display: none to display: block has no middle, so it simply switches.

Some properties are also much cheaper for the browser:

  • Best: transform and opacity. The browser can animate these without recalculating the layout, so they stay smooth even on slow phones.
  • OK: color, background-color, box-shadow, border-color. They repaint the element but do not move other things.
  • Avoid for animation: width, height, top, left, margin. Changing these forces the browser to recalculate the layout of the page on every frame.

Button and card hover effects examples

Here is a button that gently lifts and darkens when you hover it:

<button class="cta">Start learning</button>
.cta {
  background: #4f46e5;
  color: white;
  border: none;
  padding: 0.8rem 1.6rem;
  border-radius: 8px;
  font: inherit;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.cta:hover,
.cta:focus-visible {
  background: #4338ca;
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgb(79 70 229 / 0.35);
}

.cta:active {
  transform: translateY(0);
}

The button moves up 2 pixels, gets darker and shows a soft shadow. When you click, it presses back down. We also style :focus-visible, so keyboard users see the same effect.

Now a product card that lifts and zooms its image:

<article class="product-card">
  <div class="product-card__img">
    <img src="https://picsum.photos/400/300" alt="Leather backpack">
  </div>
  <h3>Leather backpack</h3>
  <p>$89.00</p>
</article>
.product-card {
  max-width: 280px;
  background: white;
  border-radius: 12px;
  padding: 1rem;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.product-card__img {
  overflow: hidden;
  border-radius: 8px;
}

.product-card img {
  display: block;
  width: 100%;
  transition: transform 0.4s ease;
}

.product-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgb(0 0 0 / 0.15);
}

.product-card:hover img {
  transform: scale(1.05);
}

The whole card rises and its shadow grows. At the same time, the image zooms in slightly. Because the image wrapper has overflow: hidden, the zoomed image stays inside its rounded box.

Practice exercise

Create a navigation bar with three links. On hover, each link should get an underline that changes color smoothly, and its background should fade to a light gray. Use a different duration for each property.

Hint: Use text-decoration-color for the underline color, and remember to put transition on the normal a rule, not on a:hover.

Key takeaways

  • A transition animates the change between two values of a property.
  • The shorthand order is: property, duration, timing function, delay.
  • Put transition on the normal state so it works in and out.
  • transform and opacity animate most smoothly; avoid animating layout properties like width or top.
  • List exact properties instead of using transition: all.