By default, when a CSS property changes, the change is instant. A button’s colour flips from blue to red the moment you hover over it, with no in-between. That sudden jump feels mechanical. CSS transitions fix this by animating the change smoothly over time, so properties glide from one value to another instead of snapping. It is one of the simplest ways to make an interface feel polished and responsive.
The Basic Idea
A transition tells the browser to animate a property whenever its value changes. You define it on the element’s normal state, then change the property somewhere else, like a hover state, and the browser fills in the motion between the two.
.button { background-color: blue; transition: background-color 0.5s ease-in-out;}.button:hover { background-color: red;}
The transition line says “whenever the background colour changes, animate it over half a second with an ease-in-out curve.” The hover rule provides the new value. The result is a button whose colour smoothly shifts from blue to red over 0.5 seconds when hovered, rather than changing instantly. The key thing to understand is that the transition lives on the base state, not the hover state, because it needs to apply in both directions, on hover and on hover-out.
The Four Transition Properties
The transition shorthand bundles four individual properties, and it helps to know what each one controls.
The transition-property specifies which property to animate, such as background-color or transform. The transition-durationsets how long the animation takes, written in seconds or milliseconds like 0.5s or 200ms. The transition-timing-functioncontrols the speed curve, shaping how the animation accelerates and decelerates. And the transition-delay holds the animation back for a set time before it begins.
Choosing What to Animate
The transition-property decides which properties respond to changes. You can name a single property, or list several separated by commas:
.box { transition-property: background-color, transform;}
This animates both the background colour and any transform. You can also use the keyword all to animate every property that changes, which is convenient but slightly less efficient, since the browser then has to watch everything rather than the specific properties you care about.
Setting the Duration
The transition-duration is how long the animation runs:
.box { transition-duration: 0.5s;}
Duration has a big effect on how an interface feels. As a rough guide, fast, snappy animations sit in the 0.2 to 0.4 second range, while smoother, more deliberate ones run from 0.5 to 1 second. Too short and the motion is barely noticeable; too long and the interface starts to feel sluggish, as if it is making the user wait.
Shaping the Motion
The transition-timing-function controls the rhythm of the animation, how it speeds up and slows down across its duration. This is what separates a natural-feeling animation from a robotic one.
The linear curve moves at a constant speed throughout, which can feel a little mechanical. The default, ease, starts slow, speeds up in the middle, and slows down at the end, which feels organic. ease-in starts slow and accelerates. ease-out starts fast and decelerates. ease-in-out combines both, starting and ending slowly with speed in between. And for full control, cubic-bezier() lets you define your own custom curve.
.box { transition: transform 1s ease-in-out;}
When the built-in curves are not quite right, a cubic-bezier gives you precise control over the acceleration:
.box { transition-timing-function: cubic-bezier(0.25, 1, 0.5, 1);}
For most interface work, ease-in-out is a reliable default because it mimics how things move in the physical world, easing into motion and easing out of it.
Delaying the Start
The transition-delay adds a pause between the moment a change is triggered and the moment the animation actually begins:
.box { transition: opacity 1s ease-in-out 0.5s;}
Here the opacity animation waits half a second before it starts. Delays are useful for staggering several animations so they happen in sequence rather than all at once, creating a more choreographed effect.
The Shorthand
Writing each property on its own line is verbose:
.box { transition-property: all; transition-duration: 0.5s; transition-timing-function: ease-in-out; transition-delay: 0s;}
The transition shorthand collapses all four into a single line:
.box { transition: all 0.5s ease-in-out 0s;}
The values are read in order: property, duration, timing function, then delay. One thing to watch is that when you include both time values, the first is always the duration and the second is the delay, so keep them in that sequence.
A Smooth Hover Button
Transitions really shine on interactive elements. Here a button changes both its colour and its size on hover, with both changes animated together:
.button { background-color: blue; color: white; padding: 10px 20px; transition: background-color 0.3s ease, transform 0.3s ease;}.button:hover { background-color: red; transform: scale(1.1);}
When hovered, the button turns red and grows slightly to 1.1 times its size, both over 0.3 seconds. Listing two transitions separated by a comma lets you animate multiple properties at once, and because the transition is on the base state, the button also animates smoothly back to normal when the cursor leaves.
A Fade-In Effect
Transitions are not limited to hover. By toggling a class with JavaScript, you can fade an element in:
.box { opacity: 0; transition: opacity 1s ease-in;}.box.show { opacity: 1;}
The box starts invisible. When the show class is added, its opacity animates from 0 to 1 over one second, producing a smooth fade-in. This is a common pattern for revealing content as it loads or as the user scrolls.
Combining Different Transitions
You can give each property its own duration and timing function within a single transition declaration:
.box { background-color: blue; width: 100px; height: 100px; transition: background-color 0.5s ease, width 0.3s linear;}.box:hover { background-color: red; width: 150px;}
On hover, the box changes both colour and width, but each animates on its own terms: the colour over half a second with an ease curve, the width over 0.3 seconds at a constant linear speed. This per-property control lets you tune exactly how each part of an element responds.
Transitions Versus Keyframe Animations
It is worth being clear about where transitions fit. A transition animates a property between two states, a start and an end, and it needs a trigger such as a hover, focus, or a class being added. That covers a huge amount of interface work: hovers, fades, expanding panels, and the like.
When you need more than two states, looping, or an animation that runs on its own without a trigger, that is where keyframe animations take over. The simple rule: reach for a transition when you are smoothly moving between two states, and reach for keyframes when two states are not enough.
The Takeaway
CSS transitions turn instant property changes into smooth, natural motion, which makes an interface feel far more polished. Define the transition on the element’s base state, set the duration to match the feel you want, choose a timing function like ease-in-out for natural movement, and add a delay when you want to stagger things. Use the shorthand to keep your code tidy, and remember that for anything beyond a two-state change you will want keyframe animations instead. For the everyday smoothing of hovers, fades, and toggles, transitions are the simplest tool that makes the biggest difference.
See you soon.
[…] are the right tool when you need multiple steps, looping, or fine control over the sequence of an animation. A bouncing ball, a spinning loader, a multi-stage attention effect, these all need […]