CSS
CSS Animation & Transition Complete Guide 2026
The differences between CSS transition and animation, how to use them, and performance optimization. A practical guide to creating smooth UI interactions.
Toolypet Team
Development Team
CSS Animation & Transition Complete Guide 2026
Button hover effects, loading spinners, page transitions... Animations are essential in modern web development.
Learn how to create smooth interactions with CSS alone, without JavaScript.
Transition vs Animation
| Feature | Transition | Animation |
|---|---|---|
| Trigger | Requires state change (:hover, etc.) | Can auto-play |
| Keyframes | Start-end only (2) | Unlimited |
| Loop | Not possible | Infinite loop possible |
| Direction | Forward only | Reverse, alternate possible |
| Use case | Simple state changes | Complex animations |
CSS Transition
Basic Syntax
transition: property duration timing-function delay;
| Property | Description | Default |
|---|---|---|
property | Property to transition | all |
duration | Duration | 0s |
timing-function | Easing curve | ease |
delay | Start delay | 0s |
Basic Examples
/* Single property */
.button {
background: #3b82f6;
transition: background 0.3s ease;
}
.button:hover {
background: #2563eb;
}
/* Multiple properties */
.card {
transform: translateY(0);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
}
Timing Functions
/* Built-in functions */
.ease { transition-timing-function: ease; } /* Default */
.linear { transition-timing-function: linear; } /* Constant speed */
.ease-in { transition-timing-function: ease-in; } /* Slow start */
.ease-out { transition-timing-function: ease-out; } /* Slow end */
.ease-in-out { transition-timing-function: ease-in-out; }
/* Custom bezier curve */
.custom { transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55); }
/* Stepped */
.steps { transition-timing-function: steps(4, end); }
Popular Bezier Curves
/* Bounce */
.bounce { transition: all 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55); }
/* Smooth */
.smooth { transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
/* Snap */
.snap { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
CSS Animation
Basic Syntax
@keyframes animationName {
from { /* Start state */ }
to { /* End state */ }
}
/* Or */
@keyframes animationName {
0% { /* Start */ }
50% { /* Middle */ }
100% { /* End */ }
}
.element {
animation: name duration timing-function delay iteration-count direction fill-mode;
}
Animation Properties
| Property | Values | Description |
|---|---|---|
animation-name | Keyframe name | Required |
animation-duration | Time | Required |
animation-timing-function | Timing | ease |
animation-delay | Delay time | 0s |
animation-iteration-count | Repeat count | 1, infinite |
animation-direction | Direction | normal, reverse, alternate |
animation-fill-mode | End state | none, forwards, backwards, both |
animation-play-state | Play state | running, paused |
Practical Animation Examples
1. Fade In
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.5s ease forwards;
}
/* Fade in from top */
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-down {
animation: fadeInDown 0.5s ease forwards;
}
2. Loading Spinner
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
3. Pulse Effect
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse {
animation: pulse 2s ease-in-out infinite;
}
/* Pulse ring */
@keyframes pulseRing {
0% {
transform: scale(0.8);
opacity: 1;
}
100% {
transform: scale(2);
opacity: 0;
}
}
.pulse-ring {
position: relative;
}
.pulse-ring::before {
content: '';
position: absolute;
inset: 0;
border: 2px solid #3b82f6;
border-radius: 50%;
animation: pulseRing 1.5s ease-out infinite;
}
4. Bounce
@keyframes bounce {
0%, 100% {
transform: translateY(0);
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
}
50% {
transform: translateY(-25%);
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
}
.bounce {
animation: bounce 1s infinite;
}
5. Shake (Error)
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.shake {
animation: shake 0.5s ease;
}
/* Trigger with JavaScript */
/* element.classList.add('shake'); */
6. Typing Effect
@keyframes typing {
from { width: 0; }
to { width: 100%; }
}
@keyframes blink {
50% { border-color: transparent; }
}
.typing {
overflow: hidden;
white-space: nowrap;
border-right: 3px solid;
width: 0;
animation:
typing 3s steps(30) forwards,
blink 0.7s step-end infinite;
}
7. Skeleton Loading
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
Using with Transform
Transform Properties
/* Translate */
transform: translateX(100px);
transform: translateY(50px);
transform: translate(100px, 50px);
/* Rotate */
transform: rotate(45deg);
transform: rotateX(45deg); /* 3D */
transform: rotateY(45deg); /* 3D */
/* Scale */
transform: scale(1.5);
transform: scaleX(2);
/* Skew */
transform: skew(10deg);
/* Combined */
transform: translateX(100px) rotate(45deg) scale(1.2);
3D Effects
/* Set perspective on parent */
.perspective-container {
perspective: 1000px;
}
/* Card flip */
.card-3d {
transform-style: preserve-3d;
transition: transform 0.6s;
}
.card-3d:hover {
transform: rotateY(180deg);
}
.card-front, .card-back {
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg);
}
Performance Optimization
GPU-Accelerated Properties
/* ✅ GPU accelerated (good performance) */
transform: translateX(100px);
transform: scale(1.1);
transform: rotate(45deg);
opacity: 0.5;
/* ❌ CPU computed (causes reflow) */
left: 100px;
width: 200px;
height: 200px;
margin: 10px;
will-change
/* Hint for animation targets */
.animated {
will-change: transform, opacity;
}
/* Only on hover */
.card:hover {
will-change: transform;
}
/* Recommended to remove after animation */
Avoiding Reflow
/* ❌ Pattern to avoid */
.bad {
animation: move 1s infinite;
}
@keyframes move {
to { left: 100px; } /* Causes reflow */
}
/* ✅ Recommended pattern */
.good {
animation: moveGood 1s infinite;
}
@keyframes moveGood {
to { transform: translateX(100px); } /* GPU accelerated */
}
Accessibility
Respecting Reduced Motion Preferences
/* When user prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Or selectively */
@media (prefers-reduced-motion: reduce) {
.animated {
animation: none;
}
.transition {
transition: none;
}
}
Practical UI Patterns
Button Hover
.button {
background: #3b82f6;
color: white;
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
}
.button:hover {
background: #2563eb;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59,130,246,0.4);
}
.button:active {
transform: translateY(0);
}
Menu Dropdown
.dropdown {
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: all 0.2s ease;
}
.menu:hover .dropdown {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
Modal Opening
.modal-overlay {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
transform: scale(0.9) translateY(20px);
transition: transform 0.3s ease;
}
.modal-overlay.active .modal {
transform: scale(1) translateY(0);
}
Staggered Appearance
.item {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.5s ease forwards;
}
.item:nth-child(1) { animation-delay: 0.1s; }
.item:nth-child(2) { animation-delay: 0.2s; }
.item:nth-child(3) { animation-delay: 0.3s; }
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
FAQ
Q1: Transition doesn't work
A: Check:
- Is the property animatable?
- Transitions don't work from
display: none - Are start and end values explicitly defined?
Q2: How to keep state after animation ends?
A: Use animation-fill-mode: forwards
.element {
animation: fadeIn 1s ease forwards;
}
Q3: Performance is poor
A:
- Only animate
transformandopacity - Use
will-changeappropriately - Avoid animating too many elements
Q4: CSS or JavaScript animations?
A:
- Simple transitions: CSS
- Complex sequences: JavaScript (GSAP, etc.)
- User input response: JavaScript
- Auto loops: CSS
Conclusion
CSS animation key points:
- Transition: Responds to state changes
- Animation: Auto-play, keyframes
- Transform: GPU accelerated, excellent performance
- Timing: ease, cubic-bezier
- Accessibility: Respect prefers-reduced-motion
Related Tools
| Tool | Purpose |
|---|---|
| Animation Generator | Generate keyframes |
| Transform Generator | Transform effects |
| Filter Generator | CSS filters |
CSSanimationtransitionanimationsUIinteraction
About the Author
Toolypet Team
Development Team
The Toolypet Team creates free, privacy-focused web tools for developers and designers. All tools run entirely in your browser with no data sent to servers.
Web DevelopmentCSS ToolsDeveloper ToolsSEOSecurity