Skip to content
Laviyx Learn

Try

CSS course menu

CSS Transitions: Smooth Hover Effects with transition-duration and Timing Functions

Learn CSS transitions: transition-property, duration, timing functions (ease, linear, cubic-bezier, steps) and delay, the transition shorthand, which properties can be animated smoothly, performance tips, and reduced motion.

Intermediate6 min readUpdated
On this page

Without transitions, a style change happens instantly: hover a button and its colour snaps. A transition animates the change smoothly over a period of time.

A first transition#

CSS
.btn {
  padding: 10px 18px;
  border: none;
  border-radius: 8px;
  background: #2f5fe0;
  color: white;
  font-size: 16px;
  transition: background-color 0.3s ease;
}
.btn:hover,
.btn:focus-visible {
  background-color: #e8590c;
}
HTML
<button class="btn">Hover over me</button>
Result

The transition is declared on the normal state, so it runs both when the hover starts and when it ends.

The four parts#

PropertyMeaningExample
transition-propertyWhich property to animatebackground-color, transform, all
transition-durationHow long0.3s, 200ms
transition-timing-functionHow the speed changes over timeease, linear, ease-in-out
transition-delayWait before starting0.1s

The shorthand lists them in this order (duration before delay): transition: transform 0.2s ease-out 0.1s;. To animate several properties, separate them with commas:

CSS
transition: background-color 0.2s, transform 0.2s ease-out;

Avoid transition: all in real projects: it animates properties you did not intend and can hurt performance.

Timing functions#

CSS
.track { position: relative; height: 34px; margin-bottom: 6px; background: #f1f3f5; border-radius: 6px; }
.dot {
  position: absolute; left: 4px; top: 4px;
  width: 110px; height: 26px; border-radius: 13px;
  background: #2f5fe0; color: white; font: 12px/26px monospace; text-align: center;
  transition: left 1.2s;
}
.demo:hover .dot { left: calc(100% - 114px); }
HTML
<div class="demo">
  <p>Hover over this box to race:</p>
  <div class="track"><div class="dot" style="transition-timing-function: linear">linear</div></div>
  <div class="track"><div class="dot" style="transition-timing-function: ease">ease</div></div>
  <div class="track"><div class="dot" style="transition-timing-function: ease-in">ease-in</div></div>
  <div class="track"><div class="dot" style="transition-timing-function: ease-out">ease-out</div></div>
  <div class="track"><div class="dot" style="transition-timing-function: steps(5)">steps(5)</div></div>
</div>
Result
FunctionFeel
ease (default)Starts fast, ends slowly
linearConstant speed (good for colour fades, spinners)
ease-inStarts slowly, speeds up (for things leaving)
ease-outStarts fast, slows down (for things entering; feels responsive)
ease-in-outSlow, fast, slow
cubic-bezier(x1, y1, x2, y2)A custom curve; browser developer tools have a curve editor
steps(n)Jumps in n steps instead of moving smoothly

What can be transitioned#

Properties with values in between two states can be animated: colours, lengths, opacity, transform, box-shadow and many more. Properties like display (block or none) switch instantly.

For smooth animation, prefer transform and opacity. Browsers can usually animate them without recalculating layout, while animating width, height, top or margin makes the browser redo the layout on every frame, which can stutter.

CSS
/* Smooth */
.card { transition: transform 0.2s ease-out; }
.card:hover { transform: translateY(-4px); }

/* Can be janky */
.card { transition: margin-top 0.2s; }
.card:hover { margin-top: -4px; }

Duration guidelines#

Small interface changes such as hover colours feel best around 150 to 300 milliseconds. Longer transitions make an interface feel slow. Larger movements across the screen may need a little more time.

Respect reduced motion#

Some people get dizzy or distracted by movement and turn on "reduce motion" in their operating system. Respect it:

CSS
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

Colour and opacity fades are usually fine to keep; it is large movement, zooming and parallax that cause problems.

Practice#

Practice exercise

Make a link whose underline grows from the left on hover and focus, using a ::after element that transitions its transform: scaleX() from 0 to 1 over 0.25s with ease-out.

Show solution
CSS
.fancy { position: relative; color: #2f5fe0; text-decoration: none; font: 600 18px system-ui, sans-serif; }
.fancy::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 100%;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease-out;
}
.fancy:hover::after,
.fancy:focus-visible::after { transform: scaleX(1); }
HTML
<a class="fancy" href="#">Hover or focus me</a>
Result

Key takeaway#

Finished this lesson?