Introduction to CSS Animations
Animations and transitions bring life to web pages, enhancing user experience by providing visual feedback, guiding attention, and creating engaging interfaces. CSS offers powerful, performant ways to animate elements without JavaScript.
Transitions Simple animations between two states, triggered by changes like hover or class toggles.
Keyframe Animations Complex, multi-step animations with precise control over the animation sequence.
Why Use CSS for Animations?
Performance CSS animations are optimized by browsers and often hardware-accelerated, making them more performant than JavaScript animations for many use cases.
Simplicity CSS animations require less code than JavaScript alternatives and are easier to implement for common animation patterns.
Declarative CSS animations are declarative, meaning you describe what should happen rather than how it should happen, making them easier to understand and maintain.
Progressive Enhancement CSS animations can be added as an enhancement without breaking functionality for users with animations disabled or unsupported browsers.
CSS Transitions
Transitions provide a way to control animation speed when changing CSS properties. Instead of having property changes take effect immediately, you can cause the changes to take place over a period of time.
Basic Syntax
.element {
/* Initial state */
opacity : 0.5 ;
transform : scale ( 1 );
/* Transition definition */
transition-property : opacity, transform;
transition-duration : 0.3 s ;
transition-timing-function : ease-in-out ;
transition-delay : 0 s ;
/* Shorthand */
/* transition: property duration timing-function delay; */
transition : opacity 0.3 s ease-in-out , transform 0.3 s ease-in-out ;
}
.element:hover {
/* Target state */
opacity : 1 ;
transform : scale ( 1.1 );
}
Transition Properties
Property Description Example Values transition-property
Specifies which CSS properties should be animated all
, opacity
, transform
, width, height
transition-duration
Defines how long the transition takes to complete 0.3s
, 300ms
, 2s
transition-timing-function
Specifies the speed curve of the transition ease
, linear
, ease-in
, ease-out
, ease-in-out
, cubic-bezier(0.1, 0.7, 1.0, 0.1)
transition-delay
Defines when the transition will start 0s
, 0.5s
, 500ms
transition
(shorthand)Combines all transition properties into one declaration all 0.3s ease 0s
, opacity 0.5s linear
Animatable Properties
Not all CSS properties can be transitioned. Here are some commonly animated properties:
width
, height
margin
, padding
top
, right
, bottom
, left
border-width
Animating layout properties can trigger browser reflow, which can be expensive for performance. Prefer using transform
when possible.
opacity
color
, background-color
box-shadow
, text-shadow
border-color
, outline-color
visibility
filter: blur()
filter: brightness()
filter: contrast()
filter: grayscale()
filter: hue-rotate()
filter: invert()
filter: opacity()
filter: saturate()
filter: sepia()
Timing Functions
Timing functions control the pace of the animation, making it more natural and engaging.
/* Predefined timing functions */
transition-timing-function : ease; /* Default: slow start, fast middle, slow end */
transition-timing-function : linear; /* Constant speed throughout */
transition-timing-function : ease-in ; /* Slow start, fast end */
transition-timing-function : ease-out ; /* Fast start, slow end */
transition-timing-function : ease-in-out ; /* Slow start and end, fast middle */
/* Custom cubic bezier curve */
transition-timing-function : cubic-bezier(0 .68 , -0 .55 , 0 .27 , 1 .55 ); /* Custom bounce effect */
/* Steps function for frame-by-frame animation */
transition-timing-function : steps(5, end); /* 5 discrete steps */
You can visualize and create custom cubic-bezier curves using tools like cubic-bezier.com .
Practical Examples
.button {
background-color : #3498db ;
color : white ;
padding : 10 px 20 px ;
border : none ;
border-radius : 4 px ;
cursor : pointer ;
transition : background-color 0.3 s ease , transform 0.2 s ease , box-shadow 0.2 s ease ;
}
.button:hover {
background-color : #2980b9 ;
transform : translateY ( -2 px );
box-shadow : 0 4 px 8 px rgba ( 0 , 0 , 0 , 0.2 );
}
.button:active {
transform : translateY ( 0 );
box-shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.2 );
}
Card Expansion
.card {
width : 300 px ;
height : 200 px ;
background-color : white ;
border-radius : 8 px ;
padding : 20 px ;
box-shadow : 0 2 px 10 px rgba ( 0 , 0 , 0 , 0.1 );
overflow : hidden ;
transition : height 0.3 s ease , box-shadow 0.3 s ease ;
}
.card:hover {
height : 300 px ;
box-shadow : 0 10 px 20 px rgba ( 0 , 0 , 0 , 0.2 );
}
.card-content {
opacity : 0 ;
max-height : 0 ;
transition : opacity 0.3 s ease , max-height 0.3 s ease ;
}
.card:hover .card-content {
opacity : 1 ;
max-height : 100 px ;
}
.nav-link {
position : relative ;
color : #333 ;
text-decoration : none ;
padding : 5 px 0 ;
}
.nav-link::after {
content : '' ;
position : absolute ;
bottom : 0 ;
left : 0 ;
width : 0 ;
height : 2 px ;
background-color : #3498db ;
transition : width 0.3 s ease ;
}
.nav-link:hover::after {
width : 100 % ;
}
CSS Keyframe Animations
Keyframe animations provide more control than transitions, allowing you to define multiple states throughout the animation sequence.
Basic Syntax
/* Define the animation */
@keyframes slide-in {
0% {
transform : translateX ( -100 % );
opacity : 0 ;
}
100% {
transform : translateX ( 0 );
opacity : 1 ;
}
}
/* Apply the animation */
.element {
animation-name : slide-in;
animation-duration : 1 s ;
animation-timing-function : ease-out ;
animation-delay : 0 s ;
animation-iteration-count : 1 ;
animation-direction : normal ;
animation-fill-mode : forwards ;
animation-play-state : running ;
/* Shorthand */
/* animation: name duration timing-function delay iteration-count direction fill-mode play-state; */
animation : slide-in 1 s ease-out 0 s 1 normal forwards running ;
}
Animation Properties
Property Description Example Values animation-name
Specifies the name of the @keyframes rule slide-in
, fade-out
, pulse
animation-duration
Defines how long the animation takes to complete one cycle 1s
, 500ms
, 2.5s
animation-timing-function
Specifies the speed curve of the animation ease
, linear
, ease-in
, ease-out
, ease-in-out
, cubic-bezier(0.1, 0.7, 1.0, 0.1)
animation-delay
Defines when the animation will start 0s
, 1s
, -0.5s
(negative values start the animation partway through)animation-iteration-count
Specifies how many times the animation should run 1
, 3
, infinite
animation-direction
Defines whether the animation should play forward, backward, or alternate normal
, reverse
, alternate
, alternate-reverse
animation-fill-mode
Specifies what values are applied before/after the animation none
, forwards
, backwards
, both
animation-play-state
Specifies whether the animation is running or paused running
, paused
animation
(shorthand)Combines all animation properties into one declaration slide-in 1s ease-out 0s 1 normal forwards
Keyframe Syntax
Keyframes define the stages and styles of the animation sequence.
/* Using percentages (recommended for most cases) */
@keyframes fade-in-out {
0% {
opacity : 0 ;
}
50% {
opacity : 1 ;
}
100% {
opacity : 0 ;
}
}
/* Using from and to (only for simple start/end animations) */
@keyframes grow {
from {
transform : scale ( 0 );
}
to {
transform : scale ( 1 );
}
}
You can define as many keyframe stages as needed, and you don’t need to include every property in every stage. CSS will interpolate between defined keyframes.
Animation Fill Mode
The animation-fill-mode
property determines what happens before the animation starts and after it ends.
Value Before Animation After Animation none
(default)Element displays in its normal state Element returns to its normal state forwards
Element displays in its normal state Element retains the computed values set by the last keyframe backwards
Element displays the computed values set by the first keyframe Element returns to its normal state both
Element displays the computed values set by the first keyframe Element retains the computed values set by the last keyframe
Practical Examples
Loading Spinner
@keyframes spin {
0% {
transform : rotate ( 0 deg );
}
100% {
transform : rotate ( 360 deg );
}
}
.spinner {
width : 40 px ;
height : 40 px ;
border : 4 px solid rgba ( 0 , 0 , 0 , 0.1 );
border-radius : 50 % ;
border-top-color : #3498db ;
animation : spin 1 s linear infinite ;
}
Pulsing Effect
@keyframes pulse {
0% {
transform : scale ( 1 );
opacity : 1 ;
}
50% {
transform : scale ( 1.1 );
opacity : 0.7 ;
}
100% {
transform : scale ( 1 );
opacity : 1 ;
}
}
.notification-badge {
display : inline-block ;
background-color : #e74c3c ;
color : white ;
border-radius : 50 % ;
padding : 5 px 10 px ;
animation : pulse 2 s ease infinite ;
}
Staggered Animation
@keyframes fade-in {
from {
opacity : 0 ;
transform : translateY ( 20 px );
}
to {
opacity : 1 ;
transform : translateY ( 0 );
}
}
.list-item {
opacity : 0 ;
animation : fade-in 0.5 s ease forwards ;
}
.list-item:nth-child ( 1 ) { animation-delay : 0.1 s ; }
.list-item:nth-child ( 2 ) { animation-delay : 0.2 s ; }
.list-item:nth-child ( 3 ) { animation-delay : 0.3 s ; }
.list-item:nth-child ( 4 ) { animation-delay : 0.4 s ; }
.list-item:nth-child ( 5 ) { animation-delay : 0.5 s ; }
Advanced Animation Techniques
Multiple Animations
You can apply multiple animations to a single element by separating them with commas.
.element {
animation :
fade-in 1 s ease forwards ,
slide-up 1.2 s ease-out forwards ,
pulse 2 s ease 1 s infinite ;
}
Animating Along a Path
With CSS offset-path
(part of the Motion Path Module), you can animate elements along a defined path.
@keyframes move-along-path {
0% {
offset-distance : 0 % ;
}
100% {
offset-distance : 100 % ;
}
}
.element {
offset-path : path( 'M 0 0 C 50 -50 50 50 100 0' );
offset-rotate : auto ;
animation : move-along-path 3 s linear infinite ;
}
You can trigger animations when elements enter the viewport using the Intersection Observer API with JavaScript.
< div class = "animate-on-scroll fade-in" > This will animate when scrolled into view </ div >
.fade-in {
opacity : 0 ;
transform : translateY ( 20 px );
transition : opacity 0.6 s ease , transform 0.6 s ease ;
}
.fade-in.visible {
opacity : 1 ;
transform : translateY ( 0 );
}
const observer = new IntersectionObserver (( entries ) => {
entries . forEach ( entry => {
if ( entry . isIntersecting ) {
entry . target . classList . add ( 'visible' );
}
});
}, { threshold: 0.1 });
document . querySelectorAll ( '.animate-on-scroll' ). forEach ( element => {
observer . observe ( element );
});
CSS Variables for Dynamic Animations
CSS Custom Properties (variables) can make animations more dynamic and configurable.
:root {
--animation-duration : 1 s ;
--animation-easing : ease-in-out ;
--animation-distance : 20 px ;
--primary-color : #3498db ;
}
@keyframes slide-in {
from {
opacity : 0 ;
transform : translateY ( var ( --animation-distance ));
}
to {
opacity : 1 ;
transform : translateY ( 0 );
}
}
.element {
animation : slide-in var ( --animation-duration ) var ( --animation-easing ) forwards ;
color : var ( --primary-color );
}
/* Adjust variables for specific elements or states */
.element.slow {
--animation-duration : 2 s ;
--animation-distance : 50 px ;
}
Animating SVG
SVG elements can be animated with CSS, offering unique possibilities for graphics animation.
< svg width = "200" height = "200" viewBox = "0 0 200 200" >
< circle class = "circle" cx = "100" cy = "100" r = "50" fill = "none" stroke = "#3498db" stroke-width = "4" />
</ svg >
@keyframes draw-circle {
0% {
stroke-dasharray : 0 314 ;
stroke-dashoffset : 0 ;
}
100% {
stroke-dasharray : 314 314 ;
stroke-dashoffset : 0 ;
}
}
.circle {
animation : draw-circle 2 s ease forwards ;
}
Animations can impact performance if not implemented carefully. Here are some best practices:
Use Hardware-Accelerated Properties
Some CSS properties are optimized for animation and can be hardware-accelerated:
These properties don’t trigger layout or paint operations, making them ideal for animations.
/* Good - uses hardware acceleration */
.element {
transform : translateX ( 100 px );
opacity : 0.5 ;
}
/* Avoid - triggers layout recalculation */
.element {
left : 100 px ;
height : 50 % ;
}
You can force an element onto its own GPU layer with will-change
or transform: translateZ(0)
.
.element {
will-change : transform, opacity;
/* Or */
transform : translateZ ( 0 );
}
Use will-change
sparingly and only for elements that will actually change. Overuse can cause memory issues.
Reduce Paint Areas
Limit the size of animated elements and use contain: paint
to isolate their paint areas.
.animated-element {
contain : paint;
animation : slide 1 s ease ;
}
Avoid Animating Expensive Properties
Some properties are particularly expensive to animate because they trigger layout recalculations:
width
, height
top
, right
, bottom
, left
margin
, padding
font-size
position
When possible, use transform
equivalents:
/* Instead of */
.element {
animation : grow 1 s ease ;
}
@keyframes grow {
from { width : 100 px ; height : 100 px ; }
to { width : 200 px ; height : 200 px ; }
}
/* Use */
.element {
width : 100 px ;
height : 100 px ;
animation : grow 1 s ease ;
}
@keyframes grow {
from { transform : scale ( 1 ); }
to { transform : scale ( 2 ); }
}
Animation Accessibility
Animations can enhance user experience but may cause issues for some users, particularly those with vestibular disorders or motion sensitivity.
Respect User Preferences
The prefers-reduced-motion
media query allows you to provide alternative animations or disable them based on user system preferences.
.element {
animation : bounce 1 s infinite ;
}
@media (prefers-reduced-motion: reduce) {
.element {
/* Disable the animation */
animation : none ;
/* Or provide a subtle alternative */
transition : opacity 0.5 s ease ;
}
}
Avoid Flashing Content
Rapid flashing or strobing effects can trigger seizures in people with photosensitive epilepsy. Avoid animations with:
Flashing more than 3 times per second
Large areas of flashing content
High contrast flashing
Provide Controls
For significant animations, provide users with controls to pause, stop, or disable animations.
< button id = "toggle-animations" > Pause Animations </ button >
const toggleButton = document . getElementById ( 'toggle-animations' );
let animationsEnabled = true ;
toggleButton . addEventListener ( 'click' , () => {
animationsEnabled = ! animationsEnabled ;
document . body . classList . toggle ( 'animations-disabled' , ! animationsEnabled );
toggleButton . textContent = animationsEnabled ? 'Pause Animations' : 'Enable Animations' ;
});
.animations-disabled * {
animation : none !important ;
transition : none !important ;
}
Browser Compatibility
Modern browsers have good support for CSS animations and transitions, but there are some considerations for older browsers.
Vendor Prefixes
For older browsers, you might need vendor prefixes. However, most modern browsers no longer require them for animations.
.element {
-webkit-animation : fade 1 s ease ;
animation : fade 1 s ease ;
}
@-webkit-keyframes fade {
from { opacity : 0 ; }
to { opacity : 1 ; }
}
@keyframes fade {
from { opacity : 0 ; }
to { opacity : 1 ; }
}
Consider using a tool like Autoprefixer to automatically add necessary vendor prefixes based on your browser support requirements.
Feature Detection
You can use feature detection to provide fallbacks for browsers that don’t support certain animation features.
if ( 'animation' in document . documentElement . style ) {
// Animations are supported
document . body . classList . add ( 'animations-enabled' );
} else {
// Animations are not supported
document . body . classList . add ( 'animations-disabled' );
}
Common Animation Patterns
Here are some reusable animation patterns for common UI elements:
Fade In
@keyframes fade-in {
from { opacity : 0 ; }
to { opacity : 1 ; }
}
.fade-in {
animation : fade-in 0.5 s ease forwards ;
}
Slide In
@keyframes slide-in-left {
from { transform : translateX ( -100 % ); }
to { transform : translateX ( 0 ); }
}
@keyframes slide-in-right {
from { transform : translateX ( 100 % ); }
to { transform : translateX ( 0 ); }
}
@keyframes slide-in-up {
from { transform : translateY ( 100 % ); }
to { transform : translateY ( 0 ); }
}
@keyframes slide-in-down {
from { transform : translateY ( -100 % ); }
to { transform : translateY ( 0 ); }
}
.slide-in-left {
animation : slide-in-left 0.5 s ease forwards ;
}
.slide-in-right {
animation : slide-in-right 0.5 s ease forwards ;
}
.slide-in-up {
animation : slide-in-up 0.5 s ease forwards ;
}
.slide-in-down {
animation : slide-in-down 0.5 s ease forwards ;
}
Bounce
@keyframes bounce {
0% , 20% , 50% , 80% , 100% {
transform : translateY ( 0 );
}
40% {
transform : translateY ( -30 px );
}
60% {
transform : translateY ( -15 px );
}
}
.bounce {
animation : bounce 1 s ease ;
}
Pulse
@keyframes pulse {
0% {
transform : scale ( 1 );
}
50% {
transform : scale ( 1.1 );
}
100% {
transform : scale ( 1 );
}
}
.pulse {
animation : pulse 1.5 s ease infinite ;
}
Shake
@keyframes shake {
0% , 100% {
transform : translateX ( 0 );
}
10% , 30% , 50% , 70% , 90% {
transform : translateX ( -10 px );
}
20% , 40% , 60% , 80% {
transform : translateX ( 10 px );
}
}
.shake {
animation : shake 0.8 s ease ;
}
Rotate
@keyframes rotate {
from {
transform : rotate ( 0 deg );
}
to {
transform : rotate ( 360 deg );
}
}
.rotate {
animation : rotate 2 s linear infinite ;
}
Flip
@keyframes flip {
0% {
transform : perspective ( 400 px ) rotateY ( 0 );
}
100% {
transform : perspective ( 400 px ) rotateY ( 360 deg );
}
}
.flip {
animation : flip 1 s ease ;
backface-visibility : visible ;
}
Animation Libraries
While CSS animations are powerful, animation libraries can provide additional features and simplify complex animations.
Popular CSS Animation Libraries
Animate.css A library of ready-to-use, cross-browser animations for use in your web projects. < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" >
< div class = "animate__animated animate__bounce" > Bouncing element </ div >
Animate.css Documentation
Magic Animations CSS3 animations with special effects. < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/magic/1.1.0/magic.min.css" >
< div class = "magictime vanishIn" > Appearing element </ div >
Magic Animations on GitHub
Hover.css A collection of CSS3 powered hover effects. < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/hover.css/2.3.1/css/hover-min.css" >
< button class = "hvr-grow" > Grow on Hover </ button >
Hover.css Documentation
CSShake CSS classes to move your DOM with shake effects. < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/csshake/1.5.3/csshake.min.css" >
< div class = "shake" > Shake me </ div >
CSShake Documentation
JavaScript Animation Libraries
For more complex animations or when you need programmatic control, JavaScript libraries can be helpful:
GSAP (GreenSock Animation Platform) Professional-grade animation for the modern web. < script src = "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js" ></ script >
< script >
gsap . to ( ".element" , {
duration: 1 ,
x: 100 ,
y: 50 ,
rotation: 360 ,
ease: "elastic"
});
</ script >
GSAP Documentation
Anime.js A lightweight JavaScript animation library. < script src = "https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" ></ script >
< script >
anime ({
targets: '.element' ,
translateX: 250 ,
rotate: '1turn' ,
duration: 800 ,
easing: 'easeInOutQuad'
});
</ script >
Anime.js Documentation
Motion One A new animation library, built on the Web Animations API for high performance. < script src = "https://cdn.jsdelivr.net/npm/motion@10.15.5/dist/motion.min.js" ></ script >
< script >
import { animate } from "motion" ;
animate ( ".element" , { x: 100 , opacity: 0 }, { duration: 1 });
</ script >
Motion One Documentation
Lottie Render After Effects animations natively in the browser. < script src = "https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.9.4/lottie.min.js" ></ script >
< div id = "animation-container" ></ div >
< script >
lottie . loadAnimation ({
container: document . getElementById ( 'animation-container' ),
renderer: 'svg' ,
loop: true ,
autoplay: true ,
path: 'animation.json'
});
</ script >
Lottie Documentation
Conclusion
CSS animations and transitions provide powerful tools for creating engaging, interactive web experiences. By understanding the fundamentals and following best practices, you can create smooth, performant animations that enhance your user interface without sacrificing accessibility or performance.
Key takeaways:
Use transitions for simple state changes and keyframe animations for more complex sequences
Prefer animating transform
and opacity
for better performance
Consider accessibility with prefers-reduced-motion
and animation controls
Optimize animations for performance by minimizing repaints and reflows
Use animation libraries when you need more complex effects or better browser support
Resources
Documentation
Further Learning