CSS provides two primary ways to animate elements: transitions and animations. Transitions allow simple animations between property states, while animations enable more complex, keyframe-based sequences. This guide covers both approaches in depth.
Transitions provide a way to smoothly change from one property value to another over a specified duration. They're ideal for simple state changes like hover effects.
/* Basic syntax */
.element {
transition: property duration timing-function delay;
}
/* Example */
.element {
transition: background-color 0.5s ease 0s;
}
.transition-color:hover {
background-color: #e74c3c;
transition: background-color 0.5s ease;
}
.transition-transform:hover {
transform: scale(1.2) rotate(10deg);
transition: transform 0.5s ease;
}
.transition-multiple:hover {
background-color: #2ecc71;
transform: translateY(-10px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
transition: all 0.5s ease;
}
.transition-delay:hover {
background-color: #e74c3c;
transition: background-color 0.5s ease 0.2s;
}
Timing functions control the pace of the transition over its duration.
transition: transform 1s linear;
Constant speed from start to finish.
transition: transform 1s ease;
Slow start, fast middle, slow end.
transition: transform 1s ease-in;
Slow start, fast end.
transition: transform 1s ease-out;
Fast start, slow end.
transition: transform 1s ease-in-out;
Slow start and end, fast middle.
transition: transform 1s cubic-bezier(0.68, -0.55, 0.27, 1.55);
Custom timing function with bounce effect.
/* Individual transition properties */
.element {
transition-property: transform, background-color;
transition-duration: 0.5s, 1s;
transition-timing-function: ease, linear;
transition-delay: 0s, 0.2s;
}
Not all CSS properties can be transitioned. Here are common animatable properties:
background-color
, background-position
border-color
, border-width
, border-radius
color
, opacity
height
, width
, margin
, padding
top
, right
, bottom
, left
letter-spacing
, word-spacing
, line-height
transform
CSS animations provide more control than transitions, allowing multiple states and more complex sequences through keyframes.
/* Define the keyframes */
@keyframes animation-name {
from {
/* CSS properties at start */
}
to {
/* CSS properties at end */
}
}
/* Apply the animation */
.element {
animation: animation-name duration timing-function delay iteration-count direction fill-mode play-state;
}
.animation-bounce {
animation: bounce 1s infinite alternate;
}
@keyframes bounce {
from {
transform: translateY(0);
}
to {
transform: translateY(-20px);
}
}
.animation-rotate {
animation: rotate 2s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.animation-pulse {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.animation-fade {
animation: fade 2s ease-in-out infinite;
}
@keyframes fade {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
.animation-shake {
animation: shake 0.5s ease-in-out infinite;
}
@keyframes shake {
0%, 100% {
transform: translateX(0);
}
25% {
transform: translateX(-10px);
}
75% {
transform: translateX(10px);
}
}
.animation-slide {
animation: slide 2s ease infinite;
}
@keyframes slide {
0% {
transform: translateX(-100%);
}
50% {
transform: translateX(50px);
}
100% {
transform: translateX(-100%);
}
}
.animation-colors {
animation: colors 3s linear infinite;
}
@keyframes colors {
0% {
background-color: #3498db;
}
25% {
background-color: #2ecc71;
}
50% {
background-color: #f1c40f;
}
75% {
background-color: #e74c3c;
}
100% {
background-color: #3498db;
}
}
.animation-combo {
animation: combo 3s ease infinite;
}
@keyframes combo {
0% {
transform: translateX(0) scale(1);
background-color: #3498db;
}
25% {
transform: translateX(50px) scale(1.1);
background-color: #2ecc71;
}
50% {
transform: translateX(0) scale(1);
background-color: #f1c40f;
}
75% {
transform: translateX(-50px) scale(1.1);
background-color: #e74c3c;
}
100% {
transform: translateX(0) scale(1);
background-color: #3498db;
}
}
These properties give you fine-grained control over your animations:
.element {
animation-duration: 1s;
}
Specifies how long the animation takes to complete one cycle.
.element {
animation-timing-function: ease-in-out;
}
Controls the pace of the animation (same options as transitions).
.element {
animation-delay: 1s;
}
Specifies a delay before the animation starts.
.element {
animation-iteration-count: 3; /* or infinite */
}
Specifies how many times the animation should run.
.element {
animation-direction: alternate;
/* normal | reverse | alternate | alternate-reverse */
}
Controls whether the animation plays forwards, backwards, or alternates.
.element {
animation-fill-mode: forwards;
/* none | forwards | backwards | both */
}
Element retains styles from the last keyframe after animation ends.
.element {
animation-fill-mode: backwards;
}
Element applies the first keyframe styles during delay period.
.element {
animation-play-state: running; /* or paused */
}
.element:hover {
animation-play-state: paused;
}
/* Using percentages for more control */
@keyframes fadeInOut {
0% {
opacity: 0;
}
25% {
opacity: 1;
}
75% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.anim-steps {
animation: steps 4s steps(4) infinite;
}
@keyframes steps {
0% {
left: 0;
}
100% {
left: calc(100% - 20px);
}
}
The steps() function creates a discrete, frame-by-frame animation instead of a smooth transition.
.staggered-item {
animation: bounce 0.5s infinite alternate;
}
.staggered-item:nth-child(2) {
animation-delay: 0.1s;
}
.staggered-item:nth-child(3) {
animation-delay: 0.2s;
}
/* And so on... */
.loader {
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.placeholder {
width: 300px;
height: 30px;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
}
@keyframes shimmer {
0% {
background-position: -100% 0;
}
100% {
background-position: 100% 0;
}
}
.notification {
padding: 10px 15px;
background-color: #2ecc71;
color: white;
border-radius: 4px;
animation: slideDown 0.5s ease forwards;
transform: translateY(-100%);
}
@keyframes slideDown {
to {
transform: translateY(0);
}
}
.btn-pulse {
padding: 10px 20px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
animation: buttonPulse 2s infinite;
}
@keyframes buttonPulse {
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);
}
}
// HTML
<div class="click-animate" id="clickBox"></div>
<button onclick="animateElement()">Animate</button>
// CSS
.bounce-animation {
animation: bounce 1s;
}
// JavaScript
function animateElement() {
const element = document.getElementById('clickBox');
element.classList.remove('bounce-animation');
// Trigger reflow
void element.offsetWidth;
element.classList.add('bounce-animation');
}
const element = document.getElementById('animatedElement');
element.addEventListener('animationstart', () => {
console.log('Animation started');
});
element.addEventListener('animationend', () => {
console.log('Animation ended');
});
element.addEventListener('animationiteration', () => {
console.log('Animation iteration');
});
/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
.respects-motion-preference {
animation: none;
transition: none;
}
}
Be considerate of users who may be sensitive to motion or have vestibular disorders by respecting the 'prefers-reduced-motion' media query.
will-change: transform, opacity;
can help browsers optimize.Modern browsers have excellent support for CSS transitions and animations. However, for older browsers, you might need vendor prefixes:
/* Prefixed transitions (for older browsers) */
.element {
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
/* Prefixed animations (for older browsers) */
@-webkit-keyframes fadeIn { /* ... */ }
@-moz-keyframes fadeIn { /* ... */ }
@-o-keyframes fadeIn { /* ... */ }
@keyframes fadeIn { /* ... */ }
.element {
-webkit-animation: fadeIn 1s;
-moz-animation: fadeIn 1s;
-o-animation: fadeIn 1s;
animation: fadeIn 1s;
}
Modern development typically uses build tools like Autoprefixer to handle these prefixes automatically.