Back to Blog
CSSAnimationKeyframesMotion

CSS Animation and Keyframes Guide

Master CSS animations with keyframes for creating engaging motion and interactions.

B
Bootspring Team
Engineering
December 18, 2018
6 min read

CSS animations enable complex motion sequences using keyframes. Here's how to create engaging animations.

Basic Keyframes#

1/* Define keyframes */ 2@keyframes fadeIn { 3 from { 4 opacity: 0; 5 } 6 to { 7 opacity: 1; 8 } 9} 10 11/* Apply animation */ 12.fade-in { 13 animation: fadeIn 1s ease-in-out; 14} 15 16/* Multiple steps */ 17@keyframes bounce { 18 0% { 19 transform: translateY(0); 20 } 21 50% { 22 transform: translateY(-20px); 23 } 24 100% { 25 transform: translateY(0); 26 } 27} 28 29.bounce { 30 animation: bounce 0.5s ease-in-out; 31}

Animation Properties#

1.animated { 2 /* Shorthand */ 3 animation: slideIn 1s ease-out 0.5s infinite alternate both; 4 5 /* Individual properties */ 6 animation-name: slideIn; 7 animation-duration: 1s; 8 animation-timing-function: ease-out; 9 animation-delay: 0.5s; 10 animation-iteration-count: infinite; 11 animation-direction: alternate; 12 animation-fill-mode: both; 13 animation-play-state: running; 14}

Timing Functions#

1/* Built-in timing functions */ 2.linear { 3 animation-timing-function: linear; 4} 5.ease { 6 animation-timing-function: ease; 7} 8.ease-in { 9 animation-timing-function: ease-in; 10} 11.ease-out { 12 animation-timing-function: ease-out; 13} 14.ease-in-out { 15 animation-timing-function: ease-in-out; 16} 17 18/* Custom cubic-bezier */ 19.custom { 20 animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); 21} 22 23/* Steps */ 24.steps { 25 animation-timing-function: steps(4, end); 26}

Fill Modes#

1/* none - no styles before/after */ 2.fill-none { 3 animation-fill-mode: none; 4} 5 6/* forwards - keep final keyframe */ 7.fill-forwards { 8 animation-fill-mode: forwards; 9} 10 11/* backwards - apply first keyframe during delay */ 12.fill-backwards { 13 animation-fill-mode: backwards; 14} 15 16/* both - combine forwards and backwards */ 17.fill-both { 18 animation-fill-mode: both; 19}

Direction Options#

1/* normal - play forward */ 2.normal { 3 animation-direction: normal; 4} 5 6/* reverse - play backward */ 7.reverse { 8 animation-direction: reverse; 9} 10 11/* alternate - forward then backward */ 12.alternate { 13 animation-direction: alternate; 14} 15 16/* alternate-reverse - backward then forward */ 17.alternate-reverse { 18 animation-direction: alternate-reverse; 19}

Practical Animations#

1/* Fade and slide */ 2@keyframes fadeSlideUp { 3 from { 4 opacity: 0; 5 transform: translateY(30px); 6 } 7 to { 8 opacity: 1; 9 transform: translateY(0); 10 } 11} 12 13.card { 14 animation: fadeSlideUp 0.5s ease-out forwards; 15} 16 17/* Staggered animation */ 18.card:nth-child(1) { animation-delay: 0.1s; } 19.card:nth-child(2) { animation-delay: 0.2s; } 20.card:nth-child(3) { animation-delay: 0.3s; } 21.card:nth-child(4) { animation-delay: 0.4s; } 22 23/* Pulse effect */ 24@keyframes pulse { 25 0%, 100% { 26 transform: scale(1); 27 } 28 50% { 29 transform: scale(1.05); 30 } 31} 32 33.pulse { 34 animation: pulse 2s ease-in-out infinite; 35}

Loading Spinners#

1/* Rotating spinner */ 2@keyframes spin { 3 to { 4 transform: rotate(360deg); 5 } 6} 7 8.spinner { 9 width: 40px; 10 height: 40px; 11 border: 3px solid #f3f3f3; 12 border-top-color: #3b82f6; 13 border-radius: 50%; 14 animation: spin 1s linear infinite; 15} 16 17/* Dots loading */ 18@keyframes dotPulse { 19 0%, 80%, 100% { 20 transform: scale(0); 21 } 22 40% { 23 transform: scale(1); 24 } 25} 26 27.loading-dots { 28 display: flex; 29 gap: 8px; 30} 31 32.loading-dots span { 33 width: 12px; 34 height: 12px; 35 background: #3b82f6; 36 border-radius: 50%; 37 animation: dotPulse 1.4s ease-in-out infinite; 38} 39 40.loading-dots span:nth-child(1) { animation-delay: -0.32s; } 41.loading-dots span:nth-child(2) { animation-delay: -0.16s; }

Attention Seekers#

1/* Shake */ 2@keyframes shake { 3 0%, 100% { transform: translateX(0); } 4 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } 5 20%, 40%, 60%, 80% { transform: translateX(5px); } 6} 7 8.shake { 9 animation: shake 0.5s ease-in-out; 10} 11 12/* Wiggle */ 13@keyframes wiggle { 14 0%, 100% { transform: rotate(0deg); } 15 25% { transform: rotate(-5deg); } 16 75% { transform: rotate(5deg); } 17} 18 19.wiggle { 20 animation: wiggle 0.3s ease-in-out; 21} 22 23/* Flash */ 24@keyframes flash { 25 0%, 50%, 100% { opacity: 1; } 26 25%, 75% { opacity: 0; } 27} 28 29.flash { 30 animation: flash 1s ease-in-out; 31}

Entrance Animations#

1/* Zoom in */ 2@keyframes zoomIn { 3 from { 4 opacity: 0; 5 transform: scale(0.5); 6 } 7 to { 8 opacity: 1; 9 transform: scale(1); 10 } 11} 12 13/* Slide from directions */ 14@keyframes slideInLeft { 15 from { 16 opacity: 0; 17 transform: translateX(-100%); 18 } 19 to { 20 opacity: 1; 21 transform: translateX(0); 22 } 23} 24 25@keyframes slideInRight { 26 from { 27 opacity: 0; 28 transform: translateX(100%); 29 } 30 to { 31 opacity: 1; 32 transform: translateX(0); 33 } 34} 35 36/* Flip */ 37@keyframes flipIn { 38 from { 39 opacity: 0; 40 transform: perspective(400px) rotateY(90deg); 41 } 42 to { 43 opacity: 1; 44 transform: perspective(400px) rotateY(0); 45 } 46}

Exit Animations#

1/* Fade out */ 2@keyframes fadeOut { 3 to { 4 opacity: 0; 5 } 6} 7 8/* Zoom out */ 9@keyframes zoomOut { 10 to { 11 opacity: 0; 12 transform: scale(0.5); 13 } 14} 15 16/* Slide out */ 17@keyframes slideOutUp { 18 to { 19 opacity: 0; 20 transform: translateY(-100%); 21 } 22}

Multiple Animations#

1/* Apply multiple animations */ 2.multi-animated { 3 animation: 4 fadeIn 0.5s ease-out, 5 slideUp 0.5s ease-out, 6 pulse 2s ease-in-out 0.5s infinite; 7} 8 9/* Sequenced with delay */ 10.sequence { 11 animation: 12 fadeIn 0.3s ease-out forwards, 13 scale 0.3s ease-out 0.3s forwards, 14 glow 0.5s ease-in-out 0.6s forwards; 15}

Animation Events (JavaScript)#

1const element = document.querySelector('.animated'); 2 3element.addEventListener('animationstart', (e) => { 4 console.log('Animation started:', e.animationName); 5}); 6 7element.addEventListener('animationend', (e) => { 8 console.log('Animation ended:', e.animationName); 9}); 10 11element.addEventListener('animationiteration', (e) => { 12 console.log('Animation iteration:', e.animationName); 13}); 14 15// Trigger animation programmatically 16function triggerAnimation(el, animationClass) { 17 el.classList.remove(animationClass); 18 void el.offsetWidth; // Force reflow 19 el.classList.add(animationClass); 20}

Performance Optimization#

1/* Use transform and opacity for best performance */ 2@keyframes optimized { 3 from { 4 opacity: 0; 5 transform: translate3d(0, 20px, 0); 6 } 7 to { 8 opacity: 1; 9 transform: translate3d(0, 0, 0); 10 } 11} 12 13/* Promote to own layer */ 14.gpu-accelerated { 15 will-change: transform, opacity; 16 transform: translateZ(0); 17} 18 19/* Avoid animating these properties */ 20.slow-animation { 21 /* Avoid: width, height, top, left, margin, padding */ 22 /* Use transform instead */ 23}

Reduced Motion#

1/* Respect user preferences */ 2@media (prefers-reduced-motion: reduce) { 3 *, 4 *::before, 5 *::after { 6 animation-duration: 0.01ms !important; 7 animation-iteration-count: 1 !important; 8 transition-duration: 0.01ms !important; 9 } 10} 11 12/* Or provide alternative */ 13@media (prefers-reduced-motion: reduce) { 14 .animated { 15 animation: none; 16 opacity: 1; 17 transform: none; 18 } 19}

Best Practices#

Performance: ✓ Animate transform and opacity ✓ Use will-change sparingly ✓ Avoid layout thrashing ✓ Use GPU acceleration Timing: ✓ Keep durations short (200-500ms) ✓ Use appropriate easing ✓ Stagger multiple elements ✓ Add subtle delays Accessibility: ✓ Respect prefers-reduced-motion ✓ Don't rely solely on animation ✓ Provide pause controls ✓ Avoid rapid flashing Avoid: ✗ Animating layout properties ✗ Too many simultaneous animations ✗ Infinite animations without purpose ✗ Motion sickness triggers

Conclusion#

CSS animations with keyframes enable rich motion design without JavaScript. Use transform and opacity for best performance, respect user preferences with prefers-reduced-motion, and keep durations short for better UX. Combine multiple animations with delays for sophisticated sequences, and always provide meaningful motion that enhances rather than distracts.

Share this article

Help spread the word about Bootspring