Topic 5: Transition & Animations
📖 5 min read · 🎯 intermediate · 🧭 Prerequisites: javascript-dom, table-forms-page-layout-introduction
Why this matters
Picture this: you click a button and — snap — it just changes. No fade, no movement, nothing. It works, sure, but something feels off. A little cold. A little cheap. You've probably noticed this on websites that feel outdated or unpolished, even if you couldn't name exactly why. That feeling is the absence of motion. CSS transition and animation are what give your UI a pulse — they guide the user's eye, signal that something happened, and make interactions feel satisfying. In this lesson, we'll learn exactly how to add that life to your designs.
What You'll Learn
- How CSS
transitionsmoothly interpolates between two states on a property change - The four components of a transition: property, duration, timing function, and delay
- How CSS
animationwith@keyframescreates multi-step, looping motion sequences - How to combine both techniques to build an animated, responsive navigation menu
The Analogy
Think of a traffic light. Without transitions, it flips instantly — red to green in a single frame, jarring and abrupt. A transition is the amber phase in between: it tells the browser "ease from this state to that one, smoothly, over this much time." A CSS animation, on the other hand, is more like a neon sign that cycles through a programmed sequence of colors on loop — it doesn't need a trigger to start or stop, it just runs, following a choreographed set of instructions you wrote in advance. Together they give you both the graceful response to user action and the autonomous ambient motion that makes an interface feel alive.
Chapter 1: Mastering CSS Transitions
Transitions are perfect for adding style to changes in page elements, like color shifts or resizing in response to a user action (hover, focus, active state).
How Transitions Work
A transition is defined by four sub-properties that you can combine into the shorthand transition property:
| Sub-property | What it controls |
|---|---|
property | Which CSS property to animate (e.g. background, transform) |
duration | How long the transition takes (e.g. 0.5s) |
timing-function | The speed curve of the motion (e.g. ease-in-out, linear) |
delay | How long to wait before the transition begins (e.g. 0.2s) |
Example: Button Background Transition
.button {
background: blue;
transition: background 0.5s ease-in-out;
}
.button:hover {
background: red;
}
When the user hovers over .button, the background color glides from blue to red over 500 milliseconds using the ease-in-out curve — accelerating into the transition and decelerating at the end. No JavaScript required.
Common Timing Functions
/* Built-in keywords */
transition-timing-function: ease; /* default — slow start, fast middle, slow end */
transition-timing-function: linear; /* constant speed */
transition-timing-function: ease-in; /* slow start */
transition-timing-function: ease-out; /* slow end */
transition-timing-function: ease-in-out;/* slow start and end */
/* Custom cubic-bezier for precise control */
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
Chapter 2: Diving Into CSS Animations
When you need more than a start and end state, animation + @keyframes let you define multiple points of transformation, enabling complex and continuous motion that runs without user interaction.
Setting Up Animations
Animations require two pieces:
@keyframesrule — defines the steps (frames) of the animationanimationproperty — attaches the keyframes to an element and controls playback
Keyframe Syntax
@keyframes color-change {
from { background-color: red; }
to { background-color: yellow; }
}
You can also use percentage stops for multi-step animations:
@keyframes color-change {
0% { background-color: red; }
50% { background-color: orange; }
100% { background-color: yellow; }
}
Applying an Animation
.animated-element {
animation: color-change 2s infinite alternate;
}
Breaking down the shorthand value:
| Value | Property it sets |
|---|---|
color-change | animation-name — which @keyframes to use |
2s | animation-duration — how long one cycle takes |
infinite | animation-iteration-count — loops forever |
alternate | animation-direction — reverses on every other cycle |
Additional Animation Properties
.animated-element {
animation-name: color-change;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: infinite; /* or a number like 3 */
animation-direction: alternate; /* normal | reverse | alternate | alternate-reverse */
animation-fill-mode: forwards; /* none | forwards | backwards | both */
animation-play-state: running; /* running | paused */
}
Chapter 3: Putting It Together — Animate Your Navigation Menu
This exercise walks through combining transitions and animations to turn a plain navigation menu into something that moves and draws the eye.
The HTML Structure
Start with a basic HTML menu:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Animated Menu</title>
</head>
<body>
<nav>
<ul>
<li class="menu-item">Home</li>
<li class="menu-item">About</li>
<li class="menu-item">Services</li>
<li class="menu-item">Contact</li>
</ul>
</nav>
</body>
</html>
Step 1: Style and Apply Transitions to Menu Items
Add CSS to give the items a smooth hover effect using transition:
ul {
list-style: none;
padding: 0;
}
.menu-item {
display: inline-block;
margin-right: 10px;
padding: 10px;
background-color: #333;
color: white;
transition: background-color 0.3s ease;
}
.menu-item:hover {
background-color: #777;
}
When the user hovers, the background glides from #333 to #777 over 300ms. Smooth and professional.
Step 2: Add a Pulse Animation on Hover
Kick it up a notch with a @keyframes animation that makes menu items scale up and back on hover:
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.menu-item:hover {
background-color: #777;
animation: pulse 1s infinite;
}
The pulse keyframes scale the element to 110% at the midpoint and return it to normal size — creating a breathing, pulsing effect that loops as long as the user holds their cursor over the item.
sequenceDiagram
participant User
participant Browser
participant CSS Engine
User->>Browser: Hover over .menu-item
Browser->>CSS Engine: Apply :hover styles
CSS Engine-->>Browser: transition: background-color 0.3s ease
CSS Engine-->>Browser: animation: pulse 1s infinite
Browser-->>User: Background fades to #777 + element pulses
User->>Browser: Move cursor away
Browser->>CSS Engine: Remove :hover
CSS Engine-->>Browser: Transition back to #333
Browser-->>User: Background fades back, pulse stops
🧪 Try It Yourself
Task: Add an entrance animation to the menu items so they fade in and slide down when the page first loads.
Your goal: Each .menu-item should start invisible and 20px above its final position, then animate into place over 0.4 seconds. Stagger each item using animation-delay so they arrive one after another.
Starter snippet:
@keyframes slide-in {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.menu-item {
animation: slide-in 0.4s ease-out forwards;
}
/* Stagger each item */
.menu-item:nth-child(1) { animation-delay: 0s; }
.menu-item:nth-child(2) { animation-delay: 0.1s; }
.menu-item:nth-child(3) { animation-delay: 0.2s; }
.menu-item:nth-child(4) { animation-delay: 0.3s; }
Success criterion: Open the page and watch all four menu items slide in from above and fade into view, one after the other. If they all appear at once or instantly, check your animation-delay values and confirm animation-fill-mode: forwards is set (or use forwards in the shorthand).
🔍 Checkpoint Quiz
Q1. What is the key difference between a CSS transition and a CSS animation?
A) Transitions require JavaScript; animations do not
B) Transitions interpolate between two states triggered by an event; animations use keyframes and can run autonomously
C) Animations only work on color properties
D) Transitions loop infinitely by default
Q2. Given the following CSS, what happens when a user hovers over .box?
.box {
width: 100px;
transition: width 1s linear;
}
.box:hover {
width: 200px;
}
A) The box instantly jumps to 200px wide
B) The box grows from 100px to 200px over 1 second at a constant speed
C) The box grows from 100px to 200px over 1 second, accelerating at the start
D) Nothing happens — transition needs an animation-name to work
Q3. What is wrong with the following keyframe animation that is meant to pulse a button?
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.button {
animation-name: pulse;
animation-iteration-count: infinite;
}
A) Percentage stops are not valid in @keyframes
B) animation-duration is missing, so the animation defaults to 0s and won't be visible
C) scale() cannot be used inside @keyframes
D) animation-iteration-count: infinite is not a valid value
Q4. How would you pause a running CSS animation on an element using only CSS (no JavaScript)?
A) Set animation-duration: 0s
B) Remove the @keyframes rule
C) Set animation-play-state: paused on the element
D) Set animation-iteration-count: 0
A1. B — Transitions react to a state change (like :hover) and interpolate between two values. Animations use @keyframes to define a sequence and can play on page load without any user interaction.
A2. B — linear is the timing function, which means constant speed throughout. The width grows from 100px to 200px over exactly 1 second at a steady rate.
A3. B — animation-duration is not set. Without a duration, the browser defaults to 0s, completing the animation instantly and making it invisible. Add animation-duration: 1s; (or a value of your choice) to fix it.
A4. C — animation-play-state: paused freezes the animation at its current frame. This can be applied on :hover to pause on interaction, or toggled via a class with JavaScript.
🪞 Recap
transitionsmoothly interpolates a CSS property between two states when a trigger (like:hover) fires, using four controls: property, duration, timing function, and delay.@keyframesdefines a named sequence of CSS states at percentage stops (0%,50%,100%orfrom/to), giving you full multi-step control over motion.- The
animationshorthand binds a@keyframesrule to an element and controls duration, iteration count, direction, and fill mode. - Combining
transition(for hover response) withanimation(for continuous motion) lets you build layered, professional UI interactions in pure CSS. animation-delayon:nth-childselectors is the simplest way to stagger entrance animations without JavaScript.
📚 Further Reading
- MDN: CSS Transitions — authoritative reference on every transition sub-property
- MDN: CSS Animations — complete guide to
@keyframesand theanimationproperty family - CSS Tricks: Animation — practical recipes and browser compatibility notes for CSS motion
- ⬅️ Previous: Table, Forms & Page Layout Introduction
- ➡️ Next: CSS Advance