CSS TRANSITIONS
Modern web pages should be both beautiful and convenient.
For example, when you hover over a button, it changes color to attract attention.
That effect can be made in different ways, but one of the simplest methods is CSS transitions.
A developer should not only know what a transition looks like.
It is also important to understand:
- how it works
- when to use it
- and what its limits are
1. Quick Roadmap
This note is easier to understand if we move through it in this order:
- what a transition is
- the four transition properties
- how to build a basic transition
- real examples
- common mistakes and practical rules
- related transform effects often used with transitions
2. What a CSS Transition Is
CSS transitions allow us to animate a change in property values.
They let us:
- set how long the change takes
- control the speed of the change
- make the change happen smoothly
A transition usually starts when an event happens, for example:
:hover:focus:active
Main idea: a transition changes a property from one value to another over time.
3. Two-State Logic
A CSS transition always works between only two states:
- the initial value
- the final value
So a transition knows how to change:
- from A to B
- and from B back to A
Example:
- button background = blue
- on hover = red
The transition changes:
- blue -> red
- and later red -> blue
Diagram:
Initial state Final state
[ blue button ] ---> [ red button ]
Return:
Mouse leaves
[ red button ] ---> [ blue button ]
Very important: CSS transitions are best for two-state animations only.
4. Important Limitation
Transitions are good when something changes between two states.
Examples:
- normal -> hover
- visible -> hidden
- small -> large
- transparent -> visible
But if an animation needs more than two states, then transitions are usually not enough.
In that case, we usually use CSS animation instead.
Simple comparison:
Transition = A <-> B
Animation = A -> B -> C -> D ...
5. The Four Transition Properties
These four properties control a transition:
transition-property: <property>;
transition-duration: <time>;
transition-timing-function: <timing-function>;
transition-delay: <delay>;
Each property controls a different part of the animation.
6. transition-property
transition-property tells the browser which CSS property should animate.
Example:
transition-property: background-color;
Meaning: animate changes of background-color.
If the background color changes, do it smoothly.
If another property changes, it will not animate unless it is also included.
Example:
.button {
transition-property: background-color;
}
Diagram:
Allowed to animate:
background-color
Not included:
width
height
opacity
Another example:
transition-property: background-color, transform;
Meaning: animate both:
- background color
- transform
7. transition-duration
If the transition duration is not specified, the property value changes instantly, without animation.
The transition-duration property sets the amount of time during which the transition should happen.
Syntax:
transition-duration: time;
Time units:
Seconds:
2s
0.5s
Milliseconds:
2000ms
500ms
Important idea: transition-duration controls how long the change takes.
Without it:
old value -> new value instantly
With it:
old value -> smooth change over time -> new value
Example:
.box {
background-color: teal;
transition-property: background-color;
transition-duration: 1000ms;
}
.box:hover {
background-color: orange;
}
What happens here:
- in the normal state the box is
teal - on hover the background color changes to
orange - because of
transition-duration: 1000ms;, the color change takes 1 second
Visual idea:
Without duration:
teal -> orange immediately
With 1000ms:
teal -> smooth color change -> orange
Comparing durations:
Very short duration:
transition-duration: 100ms;
This feels very fast.
Medium duration:
transition-duration: 300ms;
This is a common value for buttons and hover effects.
Long duration:
transition-duration: 2000ms;
This feels much slower and more noticeable.
Seconds vs milliseconds:
transition-duration: 1s;
transition-duration: 1000ms;
Also:
transition-duration: 0.5s;
transition-duration: 500ms;
Multiple durations:
If several properties have different transition times, the durations are listed with commas.
Example:
.box {
transition-property: background-color, transform;
transition-duration: 300ms, 600ms;
}
What this means:
background-colorchanges in300mstransformchanges in600ms
The first duration matches the first property. The second duration matches the second property.
Example with two properties:
.box {
background-color: teal;
transform: scale(1);
transition-property: background-color, transform;
transition-duration: 300ms, 700ms;
}
.box:hover {
background-color: orange;
transform: scale(1.2);
}
What happens:
- on hover the color changes quickly in
300ms - the size changes more slowly in
700ms
Why duration matters:
- too short -> the animation may look too sharp
- too long -> the interface may feel slow
- balanced duration -> the interface feels smooth and natural
Common practical values:
150ms200ms250ms300ms
These are very common for simple hover effects.
For larger visual effects, developers may use:
500ms700ms1000ms
Short summary: transition-duration sets how long a transition lasts. If it is not set, the change happens instantly.
Mini cheat sheet:
100ms -> very fast
300ms -> common smooth UI transition
500ms -> slower, more noticeable
1000ms -> 1 second
Very short practical rule:
For buttons and hover effects,
300ms is a very common duration.
Simple example:
.button {
background-color: blue;
transition-property: background-color;
transition-duration: 300ms;
}
.button:hover {
background-color: red;
}
Meaning:
- button starts blue
- on hover it becomes red
- the color change takes
300ms
Final summary:
- it can be written in
sorms - without it, transitions do not animate visibly
- different properties can have different durations
- duration strongly affects how smooth or fast the interface feels
8. transition-timing-function
transition-timing-function controls the speed curve.
It decides how the movement or change behaves over time.
Example:
transition-timing-function: ease;
This does not change the total time.
It changes how the speed feels during that time.
Common values:
lineareaseease-inease-outease-in-out
Quick meaning:
linear -> same speed all the time
ease -> smooth start and end
ease-in -> slow start
ease-out -> slow end
ease-in-out -> slow start and slow end
Visual idea:
linear
start ---- same speed ---- end
ease-in
start --slow--> ----faster---- end
ease-out
start ----fast---- -->slow-- end
ease-in-out
start --slow--> --fast-- -->slow-- end
9. transition-delay
transition-delay sets a waiting time before the transition begins.
Example:
transition-delay: 200ms;
Meaning:
- the event happens
- the browser waits 200ms
- then the transition starts
Diagram:
hover starts -> wait 200ms -> animation begins
This can be useful, but too much delay may make the interface feel slow.
10. How to Create a Transition
To create a transition, we usually do two things.
Step 1. Define the initial state
- set the starting property values
- set the transition properties
Step 2. Define the final state
- usually in
:hover,:focus, or another state - set the new values
This is the basic structure.
11. Basic Example
This example shows a button with a smooth color transition.
What it means:
.button-containeris the wrapper.button-itemis the actual button- the text inside the button is
Hover me
Here is the code for the button above.
<div>
<div class="button-container">
<button class="button-item">Hover me</button>
</div>
<style>
.button-container {
display: inline-block;
}
.button-container .button-item {
background-color: #4caf50;
color: white;
border: none;
padding: 14px 28px;
font-size: 18px;
border-radius: 8px;
cursor: pointer;
transition: background-color 250ms ease;
}
.button-container .button-item:hover,
.button-container .button-item:focus,
.button-container .button-item.active {
background-color: #388e3c;
}
</style>
<script>
document.querySelectorAll(".button-container .button-item").forEach((button) => {
button.addEventListener("touchstart", () => {
button.classList.add("active");
});
button.addEventListener("touchend", () => {
setTimeout(() => {
button.classList.remove("active");
}, 250);
});
});
</script>
</div>
Why this works well:
:hoversupports mouse interaction:focussupports keyboard interaction.activesupports touch interaction through JavaScript
The most important transition line is:
transition: background-color 250ms ease;
Meaning:
- animate
background-color - duration =
250ms - timing function =
ease
So the browser changes the button color smoothly instead of instantly.
Diagram:
normal state -> green
hover/focus -> darker green
transition -> smooth color change in 250ms
touch -> simulated with .active class
Mini cheat sheet:
:hover -> mouse interaction
:focus -> keyboard interaction
.active -> custom class used here for touch
transition: background-color 250ms ease;
12. Why Transitions Feel Better
Instant changes can feel sharp or rough.
Transitions make interfaces feel:
- smoother
- more modern
- easier to understand
- more interactive
Example:
Without transition:
Button:
blue -> red instantly
With transition:
Button:
blue -> smooth color change -> red
That small difference often makes the whole interface feel better.
13. Example With a Square
If the lesson says: "Hover over the square to start the transition", that usually means:
- the square has one normal style
- on hover, one or more properties change
- transition settings make the change smooth
Example:
Here is the square
Here is the code for the square
<div>
<div class="square-container">
<div class="square-box"></div>
</div>
<style>
.square-container {
display: inline-block;
}
.square-container .square-box {
width: 100px;
height: 100px;
background-color: steelblue;
transition: background-color 300ms ease;
}
.square-container .square-box:hover,
.square-container .square-box.active {
background-color: orange;
}
</style>
<script>
document.querySelectorAll(".square-container .square-box").forEach((box) => {
box.addEventListener("touchstart", () => {
box.classList.add("active");
});
box.addEventListener("touchend", () => {
setTimeout(() => {
box.classList.remove("active");
}, 300);
});
});
</script>
</div>
Diagram:
Before hover:
[ blue square ]
After hover:
[ orange square ]
Between them:
[ blue ] -> smooth change -> [ orange ]
14. Example With Multiple Properties
A transition can animate more than one property.
What happens on hover:
- the color changes from teal to orange
- the box becomes bigger
- both changes happen smoothly
Diagram:
Before hover:
[ teal box ]
During transition:
[ color changing ] + [ size increasing ]
After hover:
[ bigger orange box ]
This creates a stronger interactive effect.
Here is the code that I used
<div>
<div class="box-container">
<div class="box-item"></div>
</div>
<style>
.box-container {
display: inline-block;
}
.box-container .box-item {
width: 100px;
height: 100px;
background-color: teal;
transition-property: background-color, transform;
transition-duration: 400ms;
}
.box-container .box-item:hover,
.box-container .box-item.active {
background-color: orange;
transform: scale(1.2);
}
</style>
<script>
document.querySelectorAll(".box-container .box-item").forEach((box) => {
box.addEventListener("touchstart", () => {
box.classList.add("active");
});
box.addEventListener("touchend", () => {
setTimeout(() => {
box.classList.remove("active");
}, 400);
});
});
</script>
</div>
15. Very Important Rule
Transition settings are usually written in the initial state, not inside :hover.
Correct:
.button {
transition-property: background-color;
transition-duration: 300ms;
}
Then:
.button:hover {
background-color: red;
}
Not usually like this:
.button:hover {
transition-property: background-color;
}
Why?
Because the browser needs to know in advance how to animate the change.
Simple rule:
Normal state = transition rules
Hover state = changed values
16. Example With Delay
What happens:
- hover starts
- browser waits 200ms
- then opacity changes over 500ms
Timeline:
0ms 200ms 700ms
|----wait----|----animate----|
This can be useful for more elegant UI effects.
Here is the code:
<div>
<div class="card-container">
<div class="card-box"></div>
</div>
<style>
.card-container {
display: inline-block;
}
.card-container .card-box {
width: 120px;
height: 120px;
background-color: steelblue;
transition-property: opacity;
transition-duration: 500ms;
transition-delay: 200ms;
}
.card-container .card-box:hover,
.card-container .card-box.active {
opacity: 0.5;
}
</style>
<script>
document.querySelectorAll(".card-container .card-box").forEach((card) => {
let removeTimer;
card.addEventListener("touchstart", () => {
clearTimeout(removeTimer);
card.classList.add("active");
});
card.addEventListener("touchend", () => {
removeTimer = setTimeout(() => {
card.classList.remove("active");
}, 700);
});
});
</script>
</div>
17. Shorthand Property
Instead of writing four separate properties, we often use the shorthand:
transition: property duration timing-function delay;
Example:
transition: background-color 300ms ease 0ms;
This is the same as:
transition-property: background-color;
transition-duration: 300ms;
transition-timing-function: ease;
transition-delay: 0ms;
This shorter form is very common in real projects.
18. Very Common Button Example
.button {
background-color: #4caf50;
transition: background-color 250ms ease;
}
.button:hover {
background-color: #388e3c;
}
What happens:
- button starts green
- on hover it becomes darker green
- the change is smooth
Diagram:
Normal:
[ green button ]
Hover:
[ dark green button ]
Transition:
[ green ] -> smooth darkening -> [ dark green ]
19. Simple Full Example
HTML:
<button class="btn">Hover me</button>
CSS:
.btn {
background-color: royalblue;
color: white;
padding: 12px 20px;
border: none;
transition: background-color 300ms ease;
}
.btn:hover {
background-color: tomato;
}
What happens:
- the button starts royal blue
- on hover it turns tomato color
- the color changes smoothly over 300ms
20. Another Practical Example
Opacity example:
.image {
opacity: 1;
transition: opacity 300ms ease;
}
.image:hover {
opacity: 0.7;
}
Meaning:
- normally the image is fully visible
- on hover it becomes a little transparent
- the change is smooth
Diagram:
opacity: 1 -> smooth fade -> opacity: 0.7
21. Short Summary of the Four Transition Properties
transition-property
- what changes
transition-duration
- how long it takes
transition-timing-function
- how the speed behaves
transition-delay
- when it starts
Mini cheat sheet:
transition-property -> what changes
transition-duration -> how long it takes
transition-timing-function -> speed curve
transition-delay -> when it starts
22. Very Short Practical Rule
Put transition settings on the normal state,
and put the changed values on :hover.
This is one of the most important rules.
23. Common Mistakes
Mistake 1: putting transition rules only inside :hover.
Problem: the browser may not animate the way you expect.
Mistake 2: forgetting transition-duration.
Problem: without duration, the change happens instantly.
Mistake 3: trying to use transitions for animations with many states.
Problem: transitions are meant for two states only.
Mistake 4: using too long a duration.
Problem: the interface may feel slow or heavy.
Mistake 5: using delay everywhere.
Problem: too much waiting can make the page feel unresponsive.
24. Visual Summary
Without transition:
State A ----> State B instantly
With transition:
State A -> -> -> smooth change -> -> -> State B
Transition logic:
Initial value <----> Final value
Shorthand pattern:
transition: what how-long speed-curve delay;
25. Final Summary
CSS transitions let us smoothly animate property changes between two states.
Important things to remember:
- transitions always work between a start state and an end state
- they are often triggered by
:hover,:focus, or similar events - the main transition properties are
transition-property,transition-duration,transition-timing-function, andtransition-delay - transition settings usually belong in the normal state
- if you need more than two states, use CSS animation instead
Super short memory line:
transition = smooth change from one state to another
RELATED TRANSFORMATIONS
Transitions are often used together with CSS transformations.
That is why this second part keeps the transform notes and demos in one place.
26. What a Transformation Is
A CSS transformation changes the visual shape or position of an element.
It can:
- move the element
- rotate the element
- resize the element visually
- tilt the element
Main idea: transform changes how an element is displayed, not how the page layout is built.
27. Why Transformations Are Useful
Transformations are often used for interactive effects.
- a card rises on hover
- a button becomes slightly bigger
- an arrow icon rotates
- a photo is shifted or tilted
They are especially useful together with CSS transitions.
28. Important Idea About Layout
A transformation usually does not move other elements around it in the normal document flow.
It changes how the element is painted on the screen.
Layout position = where the browser keeps the element
Transform = how the element is visually shown
29. The Main Property
The main property is:
transform: none;
When we want to apply a transformation, we replace none with one or more transform functions.
30. The Main Transform Functions
transform: translate(...);
transform: scale(...);
transform: rotate(...);
transform: skew(...);
These functions can also be combined in one line.
31. translate() and Axis Shortcuts
translate() moves an element visually along the X and Y axes.
transform: translate(20px, 10px);
- move 20px to the right on the X axis
- move 10px down on the Y axis
Before: [box]
After: [box]
Axis-specific functions:
transform: translateX(30px);
transform: translateY(15px);
32. scale() and One-Axis Scale
scale() changes the visual size of an element.
transform: scale(1.2);
1= original size1.2= 20% bigger0.8= smaller than the original
scale(1) -> [ box ]
scale(1.2) -> [ bigger box ]
We can scale only one axis if needed:
transform: scaleX(1.5);
transform: scaleY(0.7);
This can stretch or compress the element horizontally or vertically.
33. rotate(), skew(), and transform-origin
rotate() turns an element around its transform origin.
transform: rotate(45deg);
transform: rotate(-45deg);
- positive values rotate clockwise
- negative values rotate in the opposite direction
skew() slants an element.
transform: skew(20deg, 10deg);
This effect is less common in everyday UI, but it can be useful in decorative designs.
transform-origin sets the point around which a transformation happens.
transform-origin: center;
transform-origin: top left;
This is especially important for rotation and scale.
34. Multiple Transform Functions and Order
We can combine several transform functions in one property.
transform: translateY(-10px) scale(1.05) rotate(2deg);
- move the element up
- make it slightly bigger
- rotate it a little
The order of transform functions is important.
transform: translateX(50px) rotate(45deg);
transform: rotate(45deg) translateX(50px);
same functions + different order = possibly different result
35. Visual Summary of Common Functions
translate() = move
scale() = resize visually
rotate() = turn
skew() = tilt
36. Simple Real UI Examples
Simple hover example:
HTML:
<button class="btn">Hover me</button>
CSS:
.btn {
transition: transform 300ms ease;
}
.btn:hover {
transform: scale(1.1);
}
- the button starts at normal size
- on hover it becomes a little bigger
- the change is smooth because of the transition
Card lift example:
.card {
transition: transform 250ms ease, box-shadow 250ms ease;
}
.card:hover {
transform: translateY(-8px);
}
- the card moves slightly upward
- this often makes the interface feel more interactive
Rotation example:
.icon:hover {
transform: rotate(180deg);
}
- arrow icons
- dropdown indicators
- menu toggle effects
37. Practical Rule With Transforms
Transformations often look best when combined with transitions.
.item {
transition: transform 300ms ease;
}
.item:hover {
transform: scale(1.05);
}
transform changes the visual state
transition makes that change smooth
38. Common Mistakes With Transforms
Mistake 1: expecting transform to change normal page layout.
Problem: nearby elements usually keep their original positions.
Mistake 2: forgetting that order matters in multiple transforms.
Problem: the final visual result may be different from what you expected.
Mistake 3: overusing extreme values.
Problem: too much rotation, scale, or skew can make the UI feel messy.
Mistake 4: using transformation without transition for hover effects.
Problem: the change may feel too sudden.
39. Duration Demo
Here the same hover effect uses three different durations, so you can compare how fast and slow transitions feel.
A transition is triggered when hovering over a container
Here is the code
<section>
<div class="transition-demo-wrapper">
<h1>A transition is triggered when hovering over a container</h1>
<div class="flex-container" id="transitionBox">
<div class="circle">500ms</div>
<div class="circle">1500ms</div>
<div class="circle">3000ms</div>
</div>
</div>
</section>
<style>
.transition-demo-wrapper {
font-family: Arial, sans-serif;
padding: 30px 20px;
box-sizing: border-box;
}
.transition-demo-wrapper h1 {
text-align: center;
font-size: 28px;
margin-bottom: 30px;
color: #222;
}
.transition-demo-wrapper .flex-container {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
flex-wrap: wrap;
padding: 30px;
min-height: 220px;
border: 2px dashed #ccc;
border-radius: 16px;
background: #f5f7fa;
transition: background-color 0.3s ease, border-color 0.3s ease;
cursor: pointer;
}
.transition-demo-wrapper .circle {
width: 120px;
height: 120px;
border-radius: 50%;
background: #4a90e2;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
font-weight: bold;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
transition-property: transform, background-color;
transition-timing-function: ease;
}
.transition-demo-wrapper .circle:nth-child(1) {
transition-duration: 500ms;
}
.transition-demo-wrapper .circle:nth-child(2) {
transition-duration: 1500ms;
}
.transition-demo-wrapper .circle:nth-child(3) {
transition-duration: 3000ms;
}
.transition-demo-wrapper .flex-container:hover .circle,
.transition-demo-wrapper .flex-container.active .circle {
transform: translateY(-25px) scale(1.08);
background: #ff7a59;
}
.transition-demo-wrapper .flex-container:hover,
.transition-demo-wrapper .flex-container.active {
background: #eef6ff;
border-color: #4a90e2;
}
</style>
<script>
const transitionBox = document.getElementById("transitionBox");
if (transitionBox) {
transitionBox.addEventListener("click", function () {
transitionBox.classList.toggle("active");
});
}
</script>
40. Timing Function Demo
This example compares how different timing functions change the motion, even when the duration is the same.
A transition is triggered when hovering over a container
Here is the code
<div class="transition-timing-demo-wrapper">
<h1>A transition is triggered when hovering over a container</h1>
<div class="container" id="timingDemoBox">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>
<style>
.transition-timing-demo-wrapper {
font-family: sans-serif;
text-align: center;
padding: 20px;
box-sizing: border-box;
}
.transition-timing-demo-wrapper .container {
border: 2px dashed #2a2a2a;
border-radius: 4px;
padding: 10px;
overflow: hidden;
--move-distance: 300px;
}
.transition-timing-demo-wrapper .circle {
width: 50px;
height: 50px;
border-radius: 10%;
transform: translateX(0);
transition-property: transform;
transition-duration: 2000ms;
}
.transition-timing-demo-wrapper .container:hover .circle,
.transition-timing-demo-wrapper .container.active .circle {
transform: translateX(var(--move-distance));
}
</style>
41. Delay Demo
Here is example.
Here is the code
<div class="transition-delay-demo-wrapper">
<div class="box" id="delayDemoBox"></div>
</div>
<style>
.transition-delay-demo-wrapper .box {
transition-property: background-color, transform;
transition-duration: 500ms;
transition-timing-function: ease-in-out;
transition-delay: 500ms;
}
.transition-delay-demo-wrapper .box:hover,
.transition-delay-demo-wrapper .box.active {
background-color: teal;
transform: rotate(180deg);
}
</style>