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!