Topic 5: Transition & Animations
📖 11 min read · 🎯 Intermediate · 🧭 Prerequisites: joint-query-nested-query-filtering-data, table-forms-page-layout-introduction
Why this matters
Up until now, every state change in your web page has been instant — click a button, and things just snap. No warning, no flow. Here's the thing — when something changes without any visual cue, users often think something broke. CSS transitions and animations fix exactly that. They turn those abrupt snaps into smooth, deliberate movements that feel intentional. A button that fades in, a menu that slides out, a color that shifts gradually — these small details are the difference between a page that feels built and one that feels finished.
What You'll Learn
- Understand what CSS transitions are and how to configure their four core properties
- Apply transitions to element state changes like
:hoverusingtransitionshorthand - Define multi-step animation sequences with
@keyframes - Set animation properties including duration, timing, iteration count, and direction
- Combine transitions and
@keyframesanimations on a real navigation menu
The Analogy
Think of a traffic light at a busy Vizag intersection. Without transitions, it would switch from green to red in a single violent flash — no warning, all shock. With transitions, the amber phase eases drivers from one state to the next, buying reaction time and preventing chaos. CSS transitions are that amber phase for your UI: they don't change where things end up, only how gracefully they get there. Animations go further still — imagine a neon sign that cycles through colours on its own schedule, looping indefinitely without any user action required. That autonomous, repeating motion is exactly what @keyframes animations give you.
Chapter 1: Why Motion Matters on the Web
A static page communicates information. A page with thoughtful motion communicates intent. When a button darkens smoothly on hover, the user understands it is interactive without reading a label. When a badge pulses gently, it signals urgency without a pop-up. CSS gives us two complementary tools for motion:
- Transitions — animate between two known states (normal → hovered, closed → open). Triggered by a change in CSS property value.
- Animations — animate through an arbitrary sequence of states defined by
@keyframes. Run automatically, on a timer, and can loop forever.
Neither requires JavaScript. Both are GPU-accelerated when applied to transform and opacity, keeping frame rates smooth.
Chapter 2: Mastering CSS Transitions
The Four Transition Properties
Every transition is described by four values that you can set individually or in the transition shorthand:
| Property | Purpose | Example value |
|---|---|---|
transition-property | Which CSS property to animate | background, transform, all |
transition-duration | How long the animation takes | 0.3s, 500ms |
transition-timing-function | The acceleration curve | ease, ease-in-out, linear, cubic-bezier(...) |
transition-delay | Wait before starting | 0s, 0.2s |
Shorthand Syntax
selector {
transition: <property> <duration> <timing-function> <delay>;
}
Working Example — Button Background
.button {
background: blue;
transition: background 0.5s ease-in-out;
}
.button:hover {
background: red;
}
When the cursor enters .button, the browser smoothly interpolates background from blue to red over half a second using the ease-in-out curve. When the cursor leaves, it transitions back automatically — no extra rule needed.
Common Timing Functions
/* Built-in keywords */
transition-timing-function: ease; /* slow start, fast middle, slow end */
transition-timing-function: ease-in; /* slow start */
transition-timing-function: ease-out; /* slow end */
transition-timing-function: ease-in-out; /* slow start and end */
transition-timing-function: linear; /* constant speed */
/* Custom bezier curve */
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
Transitioning Multiple Properties
.card {
transition: background-color 0.3s ease, transform 0.2s ease-out;
}
Use all as the property to catch every changing CSS value, though that can cause unintended animations — prefer listing specific properties.
Chapter 3: Diving Into CSS Animations with @keyframes
Transitions only know a start and an end. Animations know the entire journey.
Defining Keyframes
@keyframes color-change {
from {
background-color: red;
}
to {
background-color: yellow;
}
}
from is an alias for 0% and to is an alias for 100%. You can insert as many percentage stops as needed.
Applying the Animation
.animated-element {
animation: color-change 2s infinite alternate;
}
This single shorthand packs four values:
| Position | Value | Meaning |
|---|---|---|
| name | color-change | Which @keyframes block to use |
| duration | 2s | One cycle takes 2 seconds |
| iteration-count | infinite | Loop forever |
| direction | alternate | Even runs go forward, odd runs reverse |
The Full Animation Longhand Properties
.animated-element {
animation-name: color-change;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards; /* hold final keyframe state after last run */
animation-play-state: running; /* or 'paused' — controllable via JS */
}
Multi-Stop Keyframes
You are not limited to two stops. Add as many percentage breakpoints as your motion requires:
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
This pulse animation scales an element up to 110% at the midpoint, then returns it to normal size — creating a heartbeat-like effect.
Chapter 4: Putting It Together — Animate Your Navigation Menu
This full task combines an HTML navigation structure, CSS transitions on hover, and a @keyframes pulse animation.
Step 0 — HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Animated Menu</title>
<link rel="stylesheet" href="menu.css" />
</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 the Menu and Add Transitions
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;
}
The transition: background-color 0.3s ease rule means every time .menu-item leaves or enters :hover state, the background colour eases over 300 ms instead of snapping.
Step 2 — Add the Pulse Animation 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;
}
Now hovering a menu item both changes its colour (via transition) and rhythmically scales it (via animation). The 1s infinite shorthand tells the browser to repeat the scale cycle for as long as the cursor stays on the element.
Complete menu.css
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;
}
@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;
}
sequenceDiagram
participant User
participant Browser
participant CSS Engine
User->>Browser: cursor enters .menu-item
Browser->>CSS Engine: :hover state activates
CSS Engine->>Browser: transition background-color → #777 (0.3s ease)
CSS Engine->>Browser: start animation "pulse" (1s infinite)
Browser->>User: element brightens + pulses
User->>Browser: cursor leaves .menu-item
Browser->>CSS Engine: :hover state removed
CSS Engine->>Browser: transition background-color → #333 (0.3s ease)
CSS Engine->>Browser: animation "pulse" stops
Browser->>User: element dims + returns to normal scale
🧪 Try It Yourself
Task: Extend the animated menu so that each menu item slides in from the left when the page first loads.
What to build: Add a @keyframes slideIn animation that starts with the item translated -60px on the X axis (off-screen to the left) and opacity 0, ending at translateX(0) and opacity 1. Apply it to .menu-item with a 0.4s ease-out duration. Stagger each item using animation-delay — the first item gets 0s, the second 0.1s, the third 0.2s, the fourth 0.3s.
Starter snippet:
@keyframes slideIn {
from {
transform: translateX(-60px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.menu-item {
/* existing styles */
animation: slideIn 0.4s ease-out forwards;
}
.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: When you open index.html in a browser, the four menu items should cascade in from the left one after the other within the first half-second. After they arrive, hovering any item should still trigger the pulse animation from Step 2.
🔍 Checkpoint Quiz
Q1. What is the fundamental difference between a CSS transition and a CSS animation?
A) Transitions require JavaScript; animations are pure CSS
B) Transitions animate between two states triggered by a property change; animations use @keyframes to define an arbitrary sequence of states that can run independently
C) Animations only work on transform and opacity; transitions work on any property
D) Transitions are GPU-accelerated; animations always use the CPU
Q2. Given this CSS, what happens when the user hovers over .box?
.box {
background-color: blue;
transition: background-color 0.5s ease-in-out;
}
.box:hover {
background-color: red;
}
A) The background snaps instantly to red, then fades back to blue over 0.5 s when the cursor leaves
B) The background smoothly changes from blue to red over 0.5 s when hovered, and back to blue over 0.5 s when the cursor leaves
C) Nothing happens; transition must be placed on the :hover rule to take effect
D) The element disappears for 0.5 s then reappears red
Q3. Spot the bug: the developer wants the .logo element to cycle through the spin animation twice and then stop, holding its final rotated position. What is wrong?
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.logo {
animation: spin 1s linear 2;
}
A) 2 is not a valid iteration count; you must write 2 times
B) The animation will snap back to rotate(0deg) after finishing because animation-fill-mode: forwards is missing
C) @keyframes names cannot contain the word spin
D) linear is not a valid timing function inside the shorthand
Q4. How would you pause a running CSS animation on a .card element using only CSS (no JavaScript), specifically when the user hovers over it?
A) .card:hover { animation-duration: 0s; }
B) .card:hover { animation-iteration-count: 0; }
C) .card:hover { animation-play-state: paused; }
D) .card:hover { transition: none; }
A1. B — Transitions react to a CSS property change (like :hover toggling) and interpolate between exactly two values. Animations are self-contained sequences declared in @keyframes that can run automatically and loop indefinitely without any trigger.
A2. B — The transition property sits on the base rule, so the browser uses it in both directions: entering hover (blue → red, 0.5 s) and leaving hover (red → blue, 0.5 s). The transition does not need to be on the :hover rule to apply in both directions.
A3. B — Without animation-fill-mode: forwards, the browser discards the animation's final keyframe state when the animation ends, snapping the element back to its original transform. Adding forwards tells the browser to hold the last keyframe value after the last iteration completes.
A4. C — animation-play-state: paused freezes the animation at its current frame. Setting duration to 0s or iteration-count to 0 would skip or never start the animation rather than pausing a running one.
🪞 Recap
- CSS
transitionsmoothly interpolates a CSS property between two states and is defined by property, duration, timing-function, and delay. - CSS
@keyframesanimations let you choreograph multi-step motion sequences that run on their own schedule, looping and reversing independently of user interaction. - The
animationshorthand packs name, duration, timing, delay, iteration count, direction, fill-mode, and play-state into one declaration. - Transitions and animations can coexist on the same element — e.g., a colour transition on hover alongside a perpetual scale pulse.
- Keeping animations subtle (short durations, modest scale factors) respects users' attention and avoids triggering motion-sensitivity issues — always consider
prefers-reduced-motionin production code.
📚 Further Reading
- MDN: Using CSS transitions — the source of truth on transition syntax and browser support
- MDN: Using CSS animations — full
@keyframesand animation property reference - CSS Tricks: Animation — practical recipes and real-world animation patterns
- ⬅️ Previous: Table-Forms, Page-Layout Introduction
- ➡️ Next: CSS Advance