CSS Transitions
CSS transitions allow a property to change from one value to another over a specified duration with a controllable speed curve. Transitions make property changes happen gradually instead of instantly — creating simple, smooth animations.
A transition becomes active when an element's property changes — it can be triggered by pseudo-classes such as :hover, :focus, :active, or by class changes via JavaScript.
The main transition components:
- transition-property — which property to transition
- transition-duration — how long the transition lasts
- transition-timing-function — the speed curve of the transition
- transition-delay — how long to wait before the transition starts
Code Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Transitions</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f0f2f5;
padding: 40px 20px;
}
h1 {
text-align: center;
margin-bottom: 10px;
color: #1a1a2e;
}
.subtitle {
text-align: center;
color: #888;
margin-bottom: 50px;
}
.section {
max-width: 800px;
margin: 0 auto 60px;
}
.section h2 {
color: #1a1a2e;
margin-bottom: 25px;
text-align: center;
}
/* ===== Timing Functions ===== */
.timing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
}
.timing-item {
background: white;
border-radius: 12px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
}
.timing-item h4 {
font-size: 0.8rem;
color: #555;
margin-bottom: 15px;
}
.timing-track {
width: 100%;
height: 4px;
background: #e9ecef;
border-radius: 2px;
position: relative;
margin-bottom: 10px;
}
.timing-ball {
width: 20px;
height: 20px;
background: #667eea;
border-radius: 50%;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
transition-property: left;
transition-duration: 2s;
}
.timing-item:hover .timing-ball {
left: calc(100% - 20px);
}
.timing-ease .timing-ball { transition-timing-function: ease; }
.timing-linear .timing-ball { transition-timing-function: linear; }
.timing-ease-in .timing-ball { transition-timing-function: ease-in; }
.timing-ease-out .timing-ball { transition-timing-function: ease-out; }
.timing-ease-in-out .timing-ball { transition-timing-function: ease-in-out; }
.timing-cubic .timing-ball { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
/* ===== Button Hover Examples ===== */
.button-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 20px;
}
.btn {
padding: 14px 28px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
text-align: center;
}
.btn-fill {
background: #667eea;
color: white;
transition-property: background, transform, box-shadow;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.btn-fill:hover {
background: #764ba2;
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.btn-outline {
background: transparent;
color: #667eea;
border: 2px solid #667eea;
transition-property: background, color, border-color;
transition-duration: 0.3s;
}
.btn-outline:hover {
background: #667eea;
color: white;
}
.btn-scale {
background: #43e97b;
color: #1a1a2e;
transition-property: transform, box-shadow;
transition-duration: 0.2s;
transition-timing-function: ease-in-out;
}
.btn-scale:hover {
transform: scale(1.08);
box-shadow: 0 8px 25px rgba(67, 233, 123, 0.4);
}
.btn-delay {
background: #f5576c;
color: white;
transition-property: transform, box-shadow;
transition-duration: 0.3s;
transition-delay: 0s;
}
.btn-delay:hover {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 6px 20px rgba(245, 87, 108, 0.4);
transition-delay: 0.1s;
}
/* ===== Card Lift ===== */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 25px;
}
.lift-card {
background: white;
border-radius: 12px;
padding: 30px 20px;
text-align: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
transition-property: transform, box-shadow;
transition-duration: 0.4s;
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.lift-card:hover {
transform: translateY(-12px);
box-shadow: 0 12px 40px rgba(0,0,0,0.12);
}
.lift-card-icon {
font-size: 2.5rem;
margin-bottom: 15px;
}
.lift-card h3 {
color: #1a1a2e;
margin-bottom: 8px;
}
.lift-card p {
color: #666;
font-size: 0.85rem;
line-height: 1.5;
}
/* ===== Color Change ===== */
.color-grid {
display: flex;
gap: 20px;
justify-content: center;
flex-wrap: wrap;
}
.color-box {
width: 120px;
height: 120px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
cursor: pointer;
transition-property: background-color, border-radius, transform;
transition-duration: 0.5s;
transition-timing-function: ease-in-out;
}
.color-box-1 { background: #667eea; }
.color-box-2 { background: #f5576c; }
.color-box-3 { background: #43e97b; }
.color-box-4 { background: #fa709a; }
.color-box-1:hover { background: #764ba2; border-radius: 50%; transform: rotate(10deg); }
.color-box-2:hover { background: #667eea; border-radius: 50%; transform: rotate(-10deg); }
.color-box-3:hover { background: #00f2fe; border-radius: 50%; transform: scale(1.1); }
.color-box-4:hover { background: #fee140; border-radius: 50%; transform: scale(1.1); }
/* ===== Expand Animation ===== */
.expand-section {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
}
.expand-item {
border-bottom: 1px solid #eee;
}
.expand-item:last-child {
border-bottom: none;
}
.expand-header {
padding: 18px 24px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
color: #1a1a2e;
transition-property: background;
transition-duration: 0.2s;
}
.expand-header:hover {
background: #f8f9fa;
}
.expand-arrow {
font-size: 1.2rem;
transition-property: transform;
transition-duration: 0.3s;
}
.expand-item.active .expand-arrow {
transform: rotate(180deg);
}
.expand-content {
max-height: 0;
overflow: hidden;
transition-property: max-height, padding;
transition-duration: 0.4s;
transition-timing-function: ease-in-out;
}
.expand-item.active .expand-content {
max-height: 200px;
padding: 0 24px 18px;
}
.expand-content p {
color: #666;
line-height: 1.6;
font-size: 0.9rem;
}
/* ===== Multi-Property Transition ===== */
.multi-prop-section {
display: flex;
justify-content: center;
gap: 30px;
flex-wrap: wrap;
}
.multi-box {
width: 200px;
height: 200px;
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
font-weight: bold;
transition-property: all;
transition-duration: 0.5s;
}
.multi-box-1 {
background: #667eea;
color: white;
transition-delay: 0s;
}
.multi-box-2 {
background: #f5576c;
color: white;
transition-delay: 0.1s;
}
.multi-box-3 {
background: #43e97b;
color: #1a1a2e;
transition-delay: 0.2s;
}
.multi-box:hover {
transform: rotate(5deg) scale(1.05);
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.multi-box-1:hover { background: #764ba2; }
.multi-box-2:hover { background: #667eea; }
.multi-box-3:hover { background: #00f2fe; }
</style>
</head>
<body>
<h1>CSS Transitions</h1>
<p class="subtitle">Interaction: hover, click, and watch the smooth changes</p>
<div class="section">
<h2>Timing Functions</h2>
<div class="timing-grid">
<div class="timing-item timing-ease">
<h4>ease (default)</h4>
<div class="timing-track">
<div class="timing-ball"></div>
</div>
</div>
<div class="timing-item timing-linear">
<h4>linear</h4>
<div class="timing-track">
<div class="timing-ball"></div>
</div>
</div>
<div class="timing-item timing-ease-in">
<h4>ease-in</h4>
<div class="timing-track">
<div class="timing-ball"></div>
</div>
</div>
<div class="timing-item timing-ease-out">
<h4>ease-out</h4>
<div class="timing-track">
<div class="timing-ball"></div>
</div>
</div>
<div class="timing-item timing-ease-in-out">
<h4>ease-in-out</h4>
<div class="timing-track">
<div class="timing-ball"></div>
</div>
</div>
<div class="timing-item timing-cubic">
<h4>cubic-bezier (bounce)</h4>
<div class="timing-track">
<div class="timing-ball"></div>
</div>
</div>
</div>
</div>
<div class="section">
<h2>Button Hover Effects</h2>
<div class="button-grid">
<button class="btn btn-fill">Fill Change</button>
<button class="btn btn-outline">Outline to Fill</button>
<button class="btn btn-scale">Scale Up</button>
<button class="btn btn-delay">With Delay</button>
</div>
</div>
<div class="section">
<h2>Card Lift Effect</h2>
<div class="card-grid">
<div class="lift-card">
<div class="lift-card-icon">🚀</div>
<h3>Kecepatan</h3>
<p>Website loads in milliseconds</p>
</div>
<div class="lift-card">
<div class="lift-card-icon">🔒</div>
<h3>Security</h3>
<p>End-to-end data encryption</p>
</div>
<div class="lift-card">
<div class="lift-card-icon">📱</div>
<h3>Responsive</h3>
<p>Looks perfect on all devices</p>
</div>
</div>
</div>
<div class="section">
<h2>Color Change</h2>
<div class="color-grid">
<div class="color-box color-box-1">Change</div>
<div class="color-box color-box-2">Change</div>
<div class="color-box color-box-3">Change</div>
<div class="color-box color-box-4">Change</div>
</div>
</div>
<div class="section">
<h2>Expand Animation (Accordion)</h2>
<div class="expand-section">
<div class="expand-item active" onclick="this.classList.toggle('active')">
<div class="expand-header">
What are CSS Transitions?
<span class="expand-arrow">▼</span>
</div>
<div class="expand-content">
<p>CSS Transitions allow properties to change from one value to another gradually over a specified duration, creating a smooth, natural animation.</p>
</div>
</div>
<div class="expand-item" onclick="this.classList.toggle('active')">
<div class="expand-header">
When to use Transitions?
<span class="expand-arrow">▼</span>
</div>
<div class="expand-content">
<p>Transitions are perfect for simple state changes such as hover effects, focus states, and toggle animations. For complex animations with many keyframes, use CSS Animations.</p>
</div>
</div>
<div class="expand-item" onclick="this.classList.toggle('active')">
<div class="expand-header">
How to control the speed?
<span class="expand-arrow">▼</span>
</div>
<div class="expand-content">
<p>Use transition-timing-function to control the speed curve. cubic-bezier() gives the most flexible control over the acceleration and deceleration of a transition.</p>
</div>
</div>
</div>
</div>
<div class="section">
<h2>Multi-Property Transition with Delay</h2>
<div class="multi-prop-section">
<div class="multi-box multi-box-1">Box 1</div>
<div class="multi-box multi-box-2">Box 2</div>
<div class="multi-box multi-box-3">Box 3</div>
</div>
</div>
</body>
</html>
Code Explanation
transition-property
This property determines which CSS properties will be transitioned. It can be a single property, multiple properties separated by commas, or all for every property that changes.
.box {
transition-property: background; /* one property */
transition-property: background, color; /* two properties */
transition-property: all; /* all properties */
}
transition-duration
Determines how long a transition lasts. Its value is given in seconds (s) or milliseconds (ms).
.box {
transition-duration: 0.3s; /* 300 milliseconds */
transition-duration: 500ms; /* same as 0.5s */
transition-duration: 0s, 0.5s; /* delay 0 for the first property, 0.5s for the second */
}
transition-timing-function
Controls the speed curve of a transition. Some built-in values:
- ease — default. Slow at the start, fast in the middle, slow at the end
- linear — constant speed from start to finish
- ease-in — slow at the start, fast at the end
- ease-out — fast at the start, slow at the end
- ease-in-out — slow at the start and end, fast in the middle
- cubic-bezier(x1, y1, x2, y2) — full control over the curve
.box {
transition-timing-function: ease; /* default */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* bounce effect */
}
transition-delay
Determines the delay before a transition starts. Useful for creating sequential effects.
.box {
transition-delay: 0.2s; /* wait 200ms before starting */
}
Shorthand transition
All transition properties can be written on a single line using the shorthand:
/* transition: property duration timing-function delay; */
.box {
transition: all 0.3s ease 0s;
transition: background 0.3s ease, color 0.5s ease-in-out;
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.1s;
}
Transitioning Multiple Properties
You can transition several properties at once with different durations and timings:
.card {
transition:
transform 0.3s ease,
box-shadow 0.4s ease,
background 0.5s ease-in-out;
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
background: #667eea;
}
Cubic Bezier
cubic-bezier() lets you create custom curves. Parameters: (x1, y1, x2, y2) where x is time and y is progress. Tools like cubic-bezier.com help you visualize the curve.
/* Bounce effect */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* Elastic effect */
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
Everyday Use Cases
1. CTA Buttons on a Landing Page
Call-to-Action buttons use transitions to give visual feedback on hover — shifting upward, changing color, and adding a shadow:
.cta-button {
transition: transform 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}
.cta-button:hover {
transform: translateY(-3px);
background: #764ba2;
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
2. Form Input Focus
Input fields whose border and shadow change when they receive focus — giving users a clear visual indication:
.input {
border: 2px solid #ddd;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.input:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
3. Navbar Background Change
A transparent navbar that becomes solid when the user scrolls — the transition on the background makes the change feel natural:
.navbar {
background: transparent;
transition: background 0.4s ease, box-shadow 0.4s ease;
}
.navbar.scrolled {
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
4. Hamburger Menu to X
A hamburger icon that morphs into an X icon when the menu opens:
.hamburger-line {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.menu.active .hamburger-line:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.menu.active .hamburger-line:nth-child(2) {
opacity: 0;
}
.menu.active .hamburger-line:nth-child(3) {
transform: rotate(-45deg) translate(5px, -5px);
}
5. Tooltip Appearance
A tooltip that fades in gradually when the user hovers over an element:
.tooltip-text {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.tooltip:hover .tooltip-text {
opacity: 1;
visibility: visible;
}
Conclusion
CSS transitions make property changes happen gradually over a specified duration. transition-property determines which properties are transitioned, transition-duration determines how long, transition-timing-function controls the speed curve, and transition-delay adds a waiting time. The transition shorthand makes it easy to write all properties on a single line. Transitions are best suited for simple animations triggered by state changes such as hover, focus, or class toggles. For complex, self-running animations, use CSS animations with @keyframes.