Table of Contents
- Introduction to CSS Transitions
- Transition Properties
- Transition Timing Functions
- Transition Delay
- Multiple Transitions
- Transition Shorthand
- Transitionable Properties
- Interactive Transitions
- Hover Effects
- Focus and Active States
- Transform with Transitions
- Button Transitions
- Card Transitions
- Menu Transitions
- Modal Transitions
- Loading Animations
- Page Transitions
- Performance Considerations
- Accessibility
- Practical Examples
- Best Practices
- Common Mistakes
Introduction to CSS Transitions
CSS transitions allow you to change property values smoothly over a given duration. They are a fundamental tool for creating interactive and engaging user interfaces by adding subtle animations to state changes.
What are Transitions?
/* Basic transition syntax */
.element {
property: value;
transition: property duration timing-function delay;
}
/* Simple example */
.button {
background-color: blue;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: darkblue;
}
Why Use Transitions?
/* Benefits of transitions */ - Improve user experience with visual feedback - Guide user attention - Create smooth, professional interfaces - Indicate state changes - Reduce cognitive load - Add personality to your design
Transition Properties
transition-property
/* Specify which properties to transition */
.element {
transition-property: all; /* All properties (default) */
transition-property: none; /* No transitions */
transition-property: background; /* Single property */
transition-property: width, height; /* Multiple properties */
transition-property: transform, opacity; /* Common combination */
}
/* Examples */
.single-property {
transition-property: background-color;
transition-duration: 0.3s;
}
.multiple-properties {
transition-property: width, height, background;
transition-duration: 0.3s;
}
transition-duration
/* Set transition duration */
.element {
transition-duration: 0s; /* No transition (default) */
transition-duration: 0.3s; /* 300 milliseconds */
transition-duration: 200ms; /* 200 milliseconds */
transition-duration: 1s; /* 1 second */
transition-duration: 2.5s; /* 2.5 seconds */
}
/* Different durations for different properties */
.element {
transition-property: width, height, opacity;
transition-duration: 0.3s, 0.5s, 1s;
}
/* Practical examples */
.fast {
transition-duration: 0.15s; /* Fast, subtle */
}
.medium {
transition-duration: 0.3s; /* Standard */
}
.slow {
transition-duration: 0.6s; /* Slow, dramatic */
}
transition-timing-function
/* Timing functions control the acceleration curve */
.element {
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 */
transition-timing-function: step-start; /* Instant start */
transition-timing-function: step-end; /* Instant end */
}
/* Steps function */
.element {
transition-timing-function: steps(4); /* 4 discrete steps */
transition-timing-function: steps(4, start); /* Step at start */
transition-timing-function: steps(4, end); /* Step at end */
transition-timing-function: step(4, jump-start); /* Modern syntax */
}
/* Cubic bezier - custom timing */
.element {
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1); /* Ease */
transition-timing-function: cubic-bezier(0.42, 0, 1, 1); /* Ease-in */
transition-timing-function: cubic-bezier(0, 0, 0.58, 1); /* Ease-out */
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1); /* Ease-in-out */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* Bounce */
}
/* Common cubic-bezier values */
:root {
--ease-in-sine: cubic-bezier(0.47, 0, 0.745, 0.715);
--ease-out-sine: cubic-bezier(0.39, 0.575, 0.565, 1);
--ease-in-out-sine: cubic-bezier(0.445, 0.05, 0.55, 0.95);
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-back: cubic-bezier(0.6, -0.28, 0.735, 0.045);
--ease-out-back: cubic-bezier(0.175, 0.885, 0.32, 1.275);
--ease-in-out-back: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
transition-delay
/* Delay before transition starts */
.element {
transition-delay: 0s; /* No delay (default) */
transition-delay: 0.5s; /* 500ms delay */
transition-delay: 200ms; /* 200ms delay */
transition-delay: 1s; /* 1 second delay */
}
/* Different delays for different properties */
.element {
transition-property: width, height, opacity;
transition-duration: 0.3s, 0.5s, 0.3s;
transition-delay: 0s, 0.2s, 0.4s;
}
/* Staggered animations */
.parent:hover .child {
transition-delay: 0.1s;
}
.parent:hover .child:nth-child(2) {
transition-delay: 0.2s;
}
.parent:hover .child:nth-child(3) {
transition-delay: 0.3s;
}
Multiple Transitions
Transitioning Multiple Properties
/* Different transitions for different properties */
.element {
transition-property: width, height, background, opacity;
transition-duration: 0.3s, 0.5s, 0.2s, 0.4s;
transition-timing-function: ease, ease-in, ease-out, linear;
transition-delay: 0s, 0.1s, 0.2s, 0.3s;
}
/* Alternative syntax */
.element {
transition: width 0.3s ease,
height 0.5s ease-in 0.1s,
background 0.2s ease-out 0.2s,
opacity 0.4s linear 0.3s;
}
/* Practical example */
.card {
transition: transform 0.3s ease,
box-shadow 0.3s ease,
background 0.2s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
background: #f0f0f0;
}
Transition All
/* Transition all properties - use with caution */
.element {
transition: all 0.3s ease; /* Transitions everything */
}
/* Better - be specific about what to transition */
.element {
transition: transform 0.3s ease,
opacity 0.3s ease;
}
Transition Shorthand
Shorthand Syntax
/* Full shorthand: property duration timing-function delay */
.element {
transition: all 0.3s ease 0s; /* All properties */
transition: background 0.3s; /* Property + duration */
transition: opacity 0.5s ease-in; /* Property + duration + timing */
transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.2s; /* All values */
}
/* Common shorthands */
.element {
transition: 0.3s; /* Invalid - need property */
transition: all 0.3s; /* Valid - transition all */
transition: background 0.3s, transform 0.5s; /* Multiple transitions */
}
/* Examples */
.fade {
transition: opacity 0.3s ease;
}
.slide {
transition: transform 0.3s ease-out;
}
.combo {
transition: transform 0.3s ease, opacity 0.2s ease 0.1s;
}
Transitionable Properties
Commonly Transitioned Properties
/* Transform properties - best performance */
.element {
transition: transform 0.3s ease;
}
.element:hover {
transform: translateX(10px) scale(1.1) rotate(5deg);
}
/* Opacity - good performance */
.element {
transition: opacity 0.3s ease;
}
.element:hover {
opacity: 0.8;
}
/* Color properties */
.element {
transition: color 0.3s ease,
background-color 0.3s ease,
border-color 0.3s ease;
}
.element:hover {
color: #ff6b6b;
background: #f0f0f0;
border-color: #ff6b6b;
}
/* Box shadow */
.element {
transition: box-shadow 0.3s ease;
}
.element:hover {
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
/* Size properties */
.element {
transition: width 0.3s ease,
height 0.3s ease,
padding 0.3s ease;
}
.element:hover {
width: 200px;
height: 200px;
padding: 20px;
}
/* Position properties */
.element {
position: relative;
transition: top 0.3s ease, left 0.3s ease;
}
.element:hover {
top: 10px;
left: 10px;
}
Properties That Can Be Transitioned
/* Complete list of transitionable properties */ /* Layout properties */ width, height, min-width, min-height, max-width, max-height padding, margin top, right, bottom, left /* Color properties */ color, background-color, border-color, outline-color column-rule-color, text-decoration-color /* Visual properties */ opacity, visibility, box-shadow, text-shadow clip, filter, backdrop-filter /* Transform properties */ transform, transform-origin /* Border properties */ border-width, border-radius, border-spacing /* Font properties */ font-size, font-weight, line-height, letter-spacing, word-spacing /* Flex/Grid properties */ flex-grow, flex-shrink, grid-gap, gap /* Other */ z-index, order, outline-offset, perspective
Interactive Transitions
Hover Effects
/* Basic hover transition */
.button {
background: #3498db;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease;
}
.button:hover {
background: #2980b9;
transform: translateY(-2px);
}
.button:active {
transform: translateY(0);
}
/* Card hover effect */
.card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
/* Image hover effect */
.image-container {
overflow: hidden;
border-radius: 8px;
}
.image-container img {
transition: transform 0.5s ease;
}
.image-container:hover img {
transform: scale(1.1);
}
Focus and Active States
/* Focus transitions for accessibility */
.input-field {
border: 2px solid #ddd;
padding: 10px;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.input-field:focus {
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52,152,219,0.2);
outline: none;
}
/* Active state for buttons */
.button:active {
transform: scale(0.95);
transition: transform 0.1s ease;
}
/* Focus-visible for keyboard navigation */
.button:focus-visible {
outline: 3px solid #3498db;
outline-offset: 2px;
}
Custom Checkbox with Transitions
.custom-checkbox {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
}
.custom-checkbox input {
opacity: 0;
width: 0;
height: 0;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
background: #fff;
border: 2px solid #ddd;
border-radius: 4px;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.custom-checkbox input:checked ~ .checkmark {
background: #3498db;
border-color: #3498db;
}
.checkmark:after {
content: '';
position: absolute;
display: none;
left: 6px;
top: 2px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.custom-checkbox input:checked ~ .checkmark:after {
display: block;
animation: checkmark 0.2s ease-in-out;
}
@keyframes checkmark {
0% {
opacity: 0;
transform: rotate(45deg) scale(0);
}
100% {
opacity: 1;
transform: rotate(45deg) scale(1);
}
}
Transform with Transitions
2D Transforms
/* Translate - move element */
.translate {
transition: transform 0.3s ease;
}
.translate:hover {
transform: translateX(20px);
/* or */
transform: translateY(20px);
/* or */
transform: translate(20px, 20px);
}
/* Scale - resize element */
.scale {
transition: transform 0.3s ease;
}
.scale:hover {
transform: scale(1.1);
/* or */
transform: scaleX(1.2);
/* or */
transform: scaleY(1.2);
}
/* Rotate - spin element */
.rotate {
transition: transform 0.3s ease;
}
.rotate:hover {
transform: rotate(45deg);
/* or */
transform: rotate(-45deg);
}
/* Skew - skew element */
.skew {
transition: transform 0.3s ease;
}
.skew:hover {
transform: skewX(10deg);
/* or */
transform: skewY(10deg);
/* or */
transform: skew(10deg, 10deg);
}
/* Multiple transforms */
.combined {
transition: transform 0.3s ease;
}
.combined:hover {
transform: translateX(20px) scale(1.1) rotate(5deg);
}
3D Transforms
/* 3D transforms */
.perspective {
perspective: 1000px;
}
.rotate-3d {
transition: transform 0.3s ease;
}
.rotate-3d:hover {
transform: rotateX(45deg);
/* or */
transform: rotateY(45deg);
/* or */
transform: rotateZ(45deg);
/* or */
transform: rotate3d(1, 1, 1, 45deg);
}
/* 3D card flip */
.card-3d {
position: relative;
width: 200px;
height: 300px;
transform-style: preserve-3d;
transition: transform 0.6s;
}
.card-3d:hover {
transform: rotateY(180deg);
}
.card-front, .card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.card-front {
background: #3498db;
color: white;
}
.card-back {
background: #e74c3c;
color: white;
transform: rotateY(180deg);
}
Transform Origin
/* Transform origin - sets the point transforms originate from */
.element {
transform-origin: center; /* Default */
transform-origin: top left;
transform-origin: top right;
transform-origin: bottom left;
transform-origin: bottom right;
transform-origin: 50% 50%; /* Percentages */
transform-origin: 20px 30px; /* Pixels */
transform-origin: left 50%; /* Mixed */
}
/* Examples */
.scale-from-left {
transform-origin: left center;
transition: transform 0.3s ease;
}
.scale-from-left:hover {
transform: scaleX(1.5);
}
.rotate-from-top {
transform-origin: top center;
transition: transform 0.3s ease;
}
.rotate-from-top:hover {
transform: rotate(-10deg);
}
Button Transitions
Basic Button Styles
/* Base button styles */
.btn {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
}
/* Primary button */
.btn-primary {
background: #3498db;
color: white;
}
.btn-primary:hover {
background: #2980b9;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(52,152,219,0.4);
}
.btn-primary:active {
transform: translateY(0);
}
/* Secondary button */
.btn-secondary {
background: transparent;
color: #3498db;
border: 2px solid #3498db;
}
.btn-secondary:hover {
background: #3498db;
color: white;
transform: translateY(-2px);
}
/* Outline button */
.btn-outline {
background: transparent;
color: #333;
border: 2px solid #333;
position: relative;
overflow: hidden;
z-index: 1;
}
.btn-outline::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: #333;
transition: left 0.3s ease;
z-index: -1;
}
.btn-outline:hover {
color: white;
}
.btn-outline:hover::before {
left: 0;
}
Advanced Button Effects
/* Pulse effect */
.btn-pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(52,152,219,0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(52,152,219,0);
}
100% {
box-shadow: 0 0 0 0 rgba(52,152,219,0);
}
}
/* Ripple effect */
.btn-ripple {
position: relative;
overflow: hidden;
}
.btn-ripple::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 5px;
background: rgba(255,255,255,0.5);
opacity: 0;
border-radius: 100%;
transform: scale(1, 1) translate(-50%);
transform-origin: 50% 50%;
}
.btn-ripple:focus:not(:active)::after {
animation: ripple 1s ease-out;
}
@keyframes ripple {
0% {
transform: scale(0, 0);
opacity: 0.5;
}
20% {
transform: scale(25, 25);
opacity: 0.3;
}
100% {
opacity: 0;
transform: scale(40, 40);
}
}
/* Gradient button */
.btn-gradient {
background: linear-gradient(45deg, #3498db, #9b59b6);
background-size: 200% auto;
color: white;
transition: background-position 0.5s ease, transform 0.3s ease;
}
.btn-gradient:hover {
background-position: right center;
transform: translateY(-2px);
}
/* 3D button */
.btn-3d {
background: #3498db;
color: white;
border-bottom: 4px solid #2980b9;
transform: translateY(0);
}
.btn-3d:hover {
transform: translateY(-2px);
border-bottom-width: 6px;
}
.btn-3d:active {
transform: translateY(2px);
border-bottom-width: 2px;
}
/* Icon button */
.btn-icon {
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-icon i {
transition: transform 0.3s ease;
}
.btn-icon:hover i {
transform: translateX(5px);
}
/* Loading button */
.btn-loading {
position: relative;
color: transparent !important;
}
.btn-loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 3px solid white;
border-top-color: transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
Card Transitions
Card Hover Effects
/* Base card styles */
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0,0,0,0.2);
}
/* Card with image zoom */
.card-image {
overflow: hidden;
height: 200px;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.card:hover .card-image img {
transform: scale(1.1);
}
/* Card with content slide */
.card-content {
padding: 20px;
transition: transform 0.3s ease;
}
.card:hover .card-content {
transform: translateY(-10px);
}
/* Card with overlay */
.card-overlay {
position: relative;
}
.card-overlay::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
opacity: 0;
transition: opacity 0.3s ease;
z-index: 1;
}
.card-overlay:hover::before {
opacity: 1;
}
.card-overlay-content {
position: absolute;
bottom: -50px;
left: 0;
right: 0;
padding: 20px;
color: white;
transition: bottom 0.3s ease;
z-index: 2;
}
.card-overlay:hover .card-overlay-content {
bottom: 0;
}
Advanced Card Transitions
/* Card with reveal effect */
.card-reveal {
position: relative;
height: 300px;
overflow: hidden;
}
.card-reveal-front,
.card-reveal-back {
position: absolute;
width: 100%;
height: 100%;
transition: transform 0.5s ease;
}
.card-reveal-back {
background: linear-gradient(135deg, #3498db, #9b59b6);
color: white;
display: flex;
align-items: center;
justify-content: center;
transform: translateY(100%);
}
.card-reveal:hover .card-reveal-back {
transform: translateY(0);
}
/* Card with border glow */
.card-glow {
position: relative;
border: 2px solid transparent;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.card-glow:hover {
border-color: #3498db;
box-shadow: 0 0 20px rgba(52,152,219,0.3);
}
/* Card with scale and shadow */
.card-scale {
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
box-shadow 0.3s ease;
}
.card-scale:hover {
transform: scale(1.05);
box-shadow: 0 20px 30px rgba(0,0,0,0.2);
}
Menu Transitions
Dropdown Menu
/* Dropdown container */
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-trigger {
padding: 10px 20px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
min-width: 200px;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
z-index: 100;
}
.dropdown:hover .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* Dropdown items */
.dropdown-menu a {
display: block;
padding: 10px 15px;
color: #333;
text-decoration: none;
transition: background 0.2s ease, padding-left 0.2s ease;
}
.dropdown-menu a:hover {
background: #f0f0f0;
padding-left: 20px;
}
Slide-out Menu
/* Side menu */
.side-menu {
position: fixed;
top: 0;
left: -300px;
width: 300px;
height: 100vh;
background: #333;
color: white;
transition: left 0.3s ease;
z-index: 1000;
}
.side-menu.open {
left: 0;
}
.side-menu-toggle {
position: fixed;
top: 20px;
left: 20px;
z-index: 1001;
background: #3498db;
color: white;
border: none;
padding: 10px;
border-radius: 4px;
cursor: pointer;
transition: left 0.3s ease;
}
.side-menu.open ~ .side-menu-toggle {
left: 320px;
}
/* Menu items animation */
.side-menu ul {
list-style: none;
padding: 20px;
}
.side-menu li {
margin-bottom: 15px;
opacity: 0;
transform: translateX(-20px);
transition: opacity 0.3s ease, transform 0.3s ease;
transition-delay: calc(0.1s * var(--item-index));
}
.side-menu.open li {
opacity: 1;
transform: translateX(0);
}
Accordion Menu
/* Accordion */
.accordion-item {
border: 1px solid #ddd;
margin-bottom: 10px;
border-radius: 4px;
overflow: hidden;
}
.accordion-header {
padding: 15px;
background: #f5f5f5;
cursor: pointer;
font-weight: bold;
transition: background 0.3s ease;
}
.accordion-header:hover {
background: #e0e0e0;
}
.accordion-content {
max-height: 0;
padding: 0 15px;
background: white;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
}
.accordion-item.active .accordion-content {
max-height: 200px;
padding: 15px;
}
Modal Transitions
Modal Fade In
/* Modal overlay */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
z-index: 1000;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
/* Modal content */
.modal {
background: white;
border-radius: 8px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
transform: scale(0.7);
transition: transform 0.3s ease;
}
.modal-overlay.active .modal {
transform: scale(1);
}
/* Modal with slide effect */
.modal-slide {
background: white;
border-radius: 8px;
max-width: 500px;
width: 90%;
transform: translateY(-50px);
opacity: 0;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.modal-overlay.active .modal-slide {
transform: translateY(0);
opacity: 1;
}
Modal with Different Animations
/* Scale in from center */
.modal-scale {
transform: scale(0);
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.modal-overlay.active .modal-scale {
transform: scale(1);
}
/* Slide in from right */
.modal-slide-right {
position: fixed;
top: 0;
right: -500px;
width: 500px;
height: 100vh;
background: white;
transition: right 0.3s ease;
}
.modal-overlay.active .modal-slide-right {
right: 0;
}
/* Rotate in */
.modal-rotate {
transform: rotate(-10deg) scale(0.8);
opacity: 0;
transition: transform 0.4s ease, opacity 0.4s ease;
}
.modal-overlay.active .modal-rotate {
transform: rotate(0) scale(1);
opacity: 1;
}
Loading Animations
Spinner
/* Simple spinner */
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Dual ring spinner */
.spinner-dual {
width: 40px;
height: 40px;
border: 4px solid transparent;
border-top: 4px solid #3498db;
border-bottom: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s ease-in-out infinite;
}
/* Ellipsis */
.ellipsis {
display: inline-flex;
align-items: center;
}
.ellipsis span {
width: 8px;
height: 8px;
margin: 0 2px;
background: #3498db;
border-radius: 50%;
animation: ellipsis 1.4s infinite;
}
.ellipsis span:nth-child(2) {
animation-delay: 0.2s;
}
.ellipsis span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes ellipsis {
0%, 60%, 100% {
transform: scale(1);
opacity: 1;
}
30% {
transform: scale(1.5);
opacity: 0.3;
}
}
/* Pulse */
.pulse {
width: 40px;
height: 40px;
background: #3498db;
border-radius: 50%;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 1;
}
}
Progress Bar
/* Progress bar container */
.progress {
width: 100%;
height: 20px;
background: #f0f0f0;
border-radius: 10px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(45deg, #3498db, #9b59b6);
border-radius: 10px;
width: 0%;
transition: width 0.5s ease;
}
/* Animated progress */
.progress-bar.striped {
background-image: linear-gradient(
45deg,
rgba(255,255,255,0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255,255,255,0.15) 50%,
rgba(255,255,255,0.15) 75%,
transparent 75%,
transparent
);
background-size: 40px 40px;
animation: progress-stripes 1s linear infinite;
}
@keyframes progress-stripes {
0% { background-position: 0 0; }
100% { background-position: 40px 0; }
}
Page Transitions
Fade Transition
/* Page fade */
.page {
opacity: 0;
animation: fadeIn 0.5s ease forwards;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Fade out on exit */
.page-exit {
animation: fadeOut 0.5s ease forwards;
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
Slide Transitions
/* Slide in from right */
.page-slide-right {
animation: slideInRight 0.3s ease forwards;
}
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Slide in from left */
.page-slide-left {
animation: slideInLeft 0.3s ease forwards;
}
@keyframes slideInLeft {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Slide in from bottom */
.page-slide-up {
animation: slideInUp 0.3s ease forwards;
}
@keyframes slideInUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
Route Transition Example
<div class="route-container"> <div class="page home"> <h1>Home Page</h1> </div> </div>
.route-container {
position: relative;
overflow: hidden;
}
.page {
position: absolute;
width: 100%;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.page-enter {
transform: translateX(100%);
opacity: 0;
}
.page-enter-active {
transform: translateX(0);
opacity: 1;
}
.page-exit {
transform: translateX(0);
opacity: 1;
}
.page-exit-active {
transform: translateX(-100%);
opacity: 0;
}
Performance Considerations
Hardware Acceleration
/* Use transforms and opacity for smooth animations */
.good {
transform: translateX(100px);
opacity: 0.5;
transition: transform 0.3s ease, opacity 0.3s ease;
}
/* Avoid animating layout properties */
.bad {
left: 100px;
width: 200px;
transition: left 0.3s ease, width 0.3s ease; /* Causes repaints */
}
/* Hint at hardware acceleration */
.accelerated {
transform: translateZ(0);
will-change: transform;
}
/* Use will-change sparingly */
.element {
will-change: transform; /* Only when element will change */
}
.element:hover {
transform: scale(1.1);
}
Performance Tips
/* Performance best practices */
1. Use transform and opacity instead of layout properties
2. Keep animations simple
3. Avoid animating large numbers of elements
4. Use will-change sparingly
5. Test on low-powered devices
6. Consider reduced motion preferences
7. Batch property changes
8. Use requestAnimationFrame for JavaScript animations
/* Reduce motion for accessibility */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Efficient Transitions
/* Good - using transform */
.good-move {
transform: translateX(0);
transition: transform 0.3s ease;
}
.good-move:hover {
transform: translateX(10px);
}
/* Bad - using position */
.bad-move {
position: relative;
left: 0;
transition: left 0.3s ease;
}
.bad-move:hover {
left: 10px;
}
/* Opacity fade is efficient */
.fade {
opacity: 1;
transition: opacity 0.3s ease;
}
.fade:hover {
opacity: 0.8;
}
Accessibility
Reduced Motion
/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
/* Keep essential animations but make them instant */
.modal-overlay {
transition: opacity 0.01ms ease;
}
/* Remove decorative animations */
.pulse, .spinner {
animation: none !important;
}
}
/* Provide alternative experience */
@media (prefers-reduced-motion: reduce) {
.hover-effect {
transform: none !important;
transition: none !important;
}
}
Focus Indicators
/* Ensure focus is visible */
:focus-visible {
outline: 3px solid #3498db;
outline-offset: 2px;
transition: outline-offset 0.2s ease;
}
/* Animate focus indicator */
.button:focus-visible {
outline: 2px solid #3498db;
outline-offset: 4px;
}
/* Remove outline for mouse users */
:focus:not(:focus-visible) {
outline: none;
}
Screen Reader Considerations
/* Don't hide content from screen readers */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Ensure animated content is accessible */
.loading-spinner {
role: status;
aria-live: polite;
}
/* Provide alternative text for animations */
.animated-icon::before {
content: "Loading...";
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
}
Practical Examples
Example 1: Interactive Card with Multiple Transitions
<div class="interactive-card"> <div class="card-media"> <img src="image.jpg" alt="Card image"> <div class="card-overlay"> <button class="card-btn">View Details</button> </div> </div> <div class="card-content"> <h3>Card Title</h3> <p>Card description text that appears here.</p> <div class="card-meta"> <span class="date">Jan 1, 2024</span> <span class="likes">❤️ 42</span> </div> </div> </div>
.interactive-card {
width: 300px;
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
box-shadow 0.3s ease;
}
.interactive-card:hover {
transform: translateY(-10px) scale(1.02);
box-shadow: 0 20px 30px rgba(0,0,0,0.2);
}
.card-media {
position: relative;
overflow: hidden;
height: 200px;
}
.card-media img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.interactive-card:hover .card-media img {
transform: scale(1.1);
}
.card-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.interactive-card:hover .card-overlay {
opacity: 1;
}
.card-btn {
padding: 10px 20px;
background: white;
color: #333;
border: none;
border-radius: 4px;
cursor: pointer;
transform: translateY(20px);
transition: transform 0.3s ease, background 0.3s ease;
}
.interactive-card:hover .card-btn {
transform: translateY(0);
}
.card-btn:hover {
background: #3498db;
color: white;
}
.card-content {
padding: 20px;
}
.card-content h3 {
margin: 0 0 10px 0;
color: #333;
transition: color 0.3s ease;
}
.interactive-card:hover .card-content h3 {
color: #3498db;
}
.card-content p {
color: #666;
line-height: 1.6;
}
.card-meta {
display: flex;
justify-content: space-between;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eee;
color: #999;
}
.date, .likes {
transition: transform 0.2s ease;
}
.likes:hover {
transform: scale(1.2);
}
Example 2: Animated Navigation Menu
<nav class="animated-nav"> <div class="nav-brand">Logo</div> <button class="nav-toggle" aria-label="Toggle menu"> <span></span> <span></span> <span></span> </button> <ul class="nav-menu"> <li><a href="#home">Home</a></li> <li><a href="#about">About</a></li> <li><a href="#services">Services</a></li> <li><a href="#portfolio">Portfolio</a></li> <li><a href="#contact">Contact</a></li> </ul> </nav>
.animated-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: #333;
color: white;
position: relative;
}
.nav-brand {
font-size: 1.5rem;
font-weight: bold;
}
.nav-toggle {
display: none;
flex-direction: column;
justify-content: space-around;
width: 30px;
height: 25px;
background: transparent;
border: none;
cursor: pointer;
padding: 0;
z-index: 10;
}
.nav-toggle span {
width: 100%;
height: 3px;
background: white;
transition: all 0.3s ease;
}
.nav-toggle.active span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.nav-toggle.active span:nth-child(2) {
opacity: 0;
transform: translateX(-20px);
}
.nav-toggle.active span:nth-child(3) {
transform: rotate(-45deg) translate(7px, -6px);
}
.nav-menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 2rem;
}
.nav-menu a {
color: white;
text-decoration: none;
position: relative;
padding: 5px 0;
}
.nav-menu a::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: #3498db;
transition: width 0.3s ease;
}
.nav-menu a:hover::before {
width: 100%;
}
.nav-menu a::after {
content: '';
position: absolute;
bottom: -2px;
right: 0;
width: 0;
height: 2px;
background: #e74c3c;
transition: width 0.3s ease 0.1s;
}
.nav-menu a:hover::after {
width: 100%;
}
@media (max-width: 768px) {
.nav-toggle {
display: flex;
}
.nav-menu {
position: fixed;
top: 0;
right: -300px;
width: 300px;
height: 100vh;
background: #333;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2rem;
transition: right 0.3s ease;
z-index: 5;
}
.nav-menu.active {
right: 0;
}
.nav-menu a {
font-size: 1.2rem;
opacity: 0;
transform: translateX(20px);
transition: opacity 0.3s ease, transform 0.3s ease;
transition-delay: calc(0.1s * var(--item-index, 1));
}
.nav-menu.active a {
opacity: 1;
transform: translateX(0);
}
}
Example 3: Animated Form with Floating Labels
<form class="animated-form"> <div class="form-group"> <input type="text" id="name" required> <label for="name">Full Name</label> <span class="focus-border"></span> </div> <div class="form-group"> <input type="email" id="email" required> <label for="email">Email Address</label> <span class="focus-border"></span> </div> <div class="form-group"> <textarea id="message" rows="3" required></textarea> <label for="message">Message</label> <span class="focus-border"></span> </div> <button type="submit" class="submit-btn"> <span>Send Message</span> <span class="btn-icon">→</span> </button> </form>
.animated-form {
max-width: 500px;
margin: 0 auto;
padding: 2rem;
}
.form-group {
position: relative;
margin-bottom: 2rem;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 10px 0;
border: none;
border-bottom: 2px solid #ddd;
background: transparent;
font-size: 16px;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #3498db;
}
.form-group label {
position: absolute;
top: 10px;
left: 0;
color: #999;
pointer-events: none;
transition: all 0.3s ease;
}
.form-group input:focus ~ label,
.form-group input:valid ~ label,
.form-group textarea:focus ~ label,
.form-group textarea:valid ~ label {
top: -20px;
font-size: 12px;
color: #3498db;
}
.focus-border {
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: #3498db;
transition: width 0.3s ease;
}
.form-group input:focus ~ .focus-border,
.form-group textarea:focus ~ .focus-border {
width: 100%;
}
.submit-btn {
position: relative;
width: 100%;
padding: 15px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
overflow: hidden;
transition: background 0.3s ease;
}
.submit-btn:hover {
background: #2980b9;
}
.submit-btn span {
display: inline-block;
transition: transform 0.3s ease;
}
.btn-icon {
position: absolute;
right: -30px;
top: 50%;
transform: translateY(-50%);
opacity: 0;
transition: right 0.3s ease, opacity 0.3s ease;
}
.submit-btn:hover span:first-child {
transform: translateX(-10px);
}
.submit-btn:hover .btn-icon {
right: 20px;
opacity: 1;
}
.submit-btn:active {
transform: scale(0.95);
}
Example 4: Animated Modal with Multiple Effects
<button class="open-modal">Open Modal</button> <div class="modal-overlay" id="modal"> <div class="modal-container"> <div class="modal-header"> <h2>Modal Title</h2> <button class="close-modal">×</button> </div> <div class="modal-body"> <p>This is a modal with multiple animated elements.</p> <div class="modal-features"> <div class="feature">Feature 1</div> <div class="feature">Feature 2</div> <div class="feature">Feature 3</div> </div> </div> <div class="modal-footer"> <button class="modal-btn cancel">Cancel</button> <button class="modal-btn confirm">Confirm</button> </div> </div> </div>
.open-modal {
padding: 12px 24px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: transform 0.2s ease, background 0.2s ease;
}
.open-modal:hover {
background: #2980b9;
transform: scale(1.05);
}
.open-modal:active {
transform: scale(0.95);
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
z-index: 1000;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal-container {
background: white;
border-radius: 8px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
transform: scale(0.7) translateY(-50px);
opacity: 0;
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
opacity 0.3s ease;
}
.modal-overlay.active .modal-container {
transform: scale(1) translateY(0);
opacity: 1;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid #eee;
}
.close-modal {
background: none;
border: none;
font-size: 30px;
cursor: pointer;
color: #999;
transition: color 0.2s ease, transform 0.2s ease;
}
.close-modal:hover {
color: #e74c3c;
transform: rotate(90deg);
}
.modal-body {
padding: 20px;
}
.modal-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 15px;
margin-top: 20px;
}
.feature {
padding: 15px;
background: #f5f5f5;
border-radius: 4px;
text-align: center;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.3s ease, transform 0.3s ease;
transition-delay: calc(0.1s * var(--feature-index));
}
.modal-overlay.active .feature {
opacity: 1;
transform: translateY(0);
}
.modal-footer {
padding: 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 10px;
}
.modal-btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
}
.modal-btn.cancel {
background: #95a5a6;
color: white;
}
.modal-btn.cancel:hover {
background: #7f8c8d;
transform: translateY(-2px);
}
.modal-btn.confirm {
background: #3498db;
color: white;
}
.modal-btn.confirm:hover {
background: #2980b9;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(52,152,219,0.4);
}
.modal-btn:active {
transform: translateY(0);
}
Best Practices
Transition Guidelines
/* 1. Keep transitions subtle */
.good {
transition: transform 0.2s ease;
}
.bad {
transition: transform 2s ease; /* Too slow */
}
/* 2. Be consistent with timing */
:root {
--transition-fast: 0.15s;
--transition-base: 0.3s;
--transition-slow: 0.5s;
}
.element {
transition: transform var(--transition-base) ease;
}
/* 3. Use appropriate timing functions */
.element {
transition: transform 0.3s ease-out; /* Natural feel */
}
/* 4. Consider mobile performance */
@media (max-width: 768px) {
.element {
transition-duration: 0.15s; /* Faster on mobile */
}
}
/* 5. Group related transitions */
.element {
transition: transform 0.3s ease,
opacity 0.3s ease,
box-shadow 0.3s ease;
}
Transition Checklist
/* Transition design checklist */ - [ ] Duration is appropriate (not too slow/fast) - [ ] Timing function matches the feel - [ ] Only transition necessary properties - [ ] Test on mobile devices - [ ] Consider reduced motion preferences - [ ] Ensure focus states are visible - [ ] Don't trigger accessibility issues - [ ] Test with screen readers - [ ] Performance is optimized - [ ] Consistent across similar elements
Accessibility Checklist
/* Accessibility checklist */ - [ ] Respect prefers-reduced-motion - [ ] Ensure focus indicators are visible - [ ] Don't hide important content - [ ] Provide alternative for animations - [ ] Test with keyboard navigation - [ ] Ensure proper contrast during transitions - [ ] Don't cause seizures (avoid flashing)
Common Mistakes
Mistake 1: Transitioning All Properties
/* Bad */
.element {
transition: all 0.3s ease; /* Performance hit */
}
/* Good */
.element {
transition: transform 0.3s ease, opacity 0.3s ease;
}
Mistake 2: No Fallback for Reduced Motion
/* Bad */
.element {
animation: spin 1s infinite;
}
/* Good */
.element {
animation: spin 1s infinite;
}
@media (prefers-reduced-motion: reduce) {
.element {
animation: none;
}
}
Mistake 3: Animating Expensive Properties
/* Bad - causes repaints */
.element {
left: 0;
transition: left 0.3s ease;
}
/* Good - hardware accelerated */
.element {
transform: translateX(0);
transition: transform 0.3s ease;
}
Mistake 4: Transitions Too Slow or Fast
/* Bad - too slow */
.element {
transition: transform 2s ease;
}
/* Bad - too fast */
.element {
transition: transform 0.05s ease;
}
/* Good */
.element {
transition: transform 0.3s ease;
}
Mistake 5: No Focus Styles
/* Bad - removes focus indicator */
.element:focus {
outline: none;
}
/* Good - custom focus style */
.element:focus-visible {
outline: 3px solid #3498db;
outline-offset: 2px;
}
Mistake 6: Transitioning Display
/* Bad - display can't be transitioned */
.element {
display: none;
transition: display 0.3s ease; /* Won't work */
}
/* Good - use opacity + visibility */
.element {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.element.active {
opacity: 1;
visibility: visible;
}
Mistake 7: Forgetting to Set Initial Values
/* Bad - no initial value */
.element {
transition: transform 0.3s ease;
}
.element:hover {
transform: scale(1.1);
}
/* Good - initial value set */
.element {
transform: scale(1);
transition: transform 0.3s ease;
}
.element:hover {
transform: scale(1.1);
}
Conclusion
CSS transitions are powerful tools for creating smooth, engaging user interfaces. This comprehensive guide covered:
Key Takeaways
- Transition Properties - property, duration, timing-function, delay
- Timing Functions - ease, linear, ease-in, ease-out, cubic-bezier
- Performance - use transform and opacity, avoid layout properties
- Accessibility - respect reduced motion, maintain focus indicators
- Interactive Elements - buttons, cards, menus, modals
- Practical Examples - real-world implementations
- Best Practices - consistency, subtlety, performance
Best Practices Summary
- Use appropriate durations - 0.2s to 0.4s for most interactions
- Choose natural timing functions - ease-out for entrances, ease-in for exits
- Limit transitioned properties - only transform and opacity for performance
- Test on real devices - ensure smoothness on mobile
- Consider accessibility - respect user preferences
- Be consistent - use similar transitions throughout your site
Common Use Cases
| Component | Properties | Duration | Timing |
|---|---|---|---|
| Buttons | transform, background | 0.2s | ease |
| Cards | transform, box-shadow | 0.3s | ease-out |
| Modals | opacity, transform | 0.3s | ease-out |
| Dropdowns | opacity, transform | 0.2s | ease |
| Tooltips | opacity, transform | 0.15s | ease |
Remember: Good transitions are subtle, purposeful, and enhance the user experience without being distracting!