Animation Examples
CSS keyframe animations let you create complex animations with full control. From a simple loading spinner to a dramatic entrance animation, keyframes provide a declarative way to bring an interface to life.
CSS animations are more performant than JavaScript because they can be GPU-accelerated. But use them wisely — too many animations can disturb the UX and drain a mobile device's battery.
The examples below cover animations that are commonly used in everyday web development.
Code Example
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
font-family: 'Segoe UI', Arial, sans-serif;
padding: 40px 20px;
background: #1a1a2e;
color: white;
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
}
.demo-card {
background: rgba(255,255,255,0.08);
padding: 30px;
border-radius: 16px;
width: 300px;
text-align: center;
}
.demo-card h3 {
margin-top: 0;
font-size: 1em;
color: #aaa;
margin-bottom: 20px;
}
/* === 1. Loading Spinner === */
.spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(255,255,255,0.1);
border-top-color: #667eea;
border-radius: 50%;
margin: 0 auto;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* === 2. Dual Ring Spinner === */
.dual-ring {
width: 50px;
height: 50px;
border: 4px solid transparent;
border-top-color: #f093fb;
border-bottom-color: #f093fb;
border-radius: 50%;
margin: 0 auto;
animation: spin 1s linear infinite;
position: relative;
}
.dual-ring::before {
content: '';
position: absolute;
inset: 6px;
border: 4px solid transparent;
border-left-color: #f5576c;
border-right-color: #f5576c;
border-radius: 50%;
animation: spin 0.6s linear infinite reverse;
}
/* === 3. Typing Cursor === */
.typing-container {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
}
.typing-text {
font-size: 1.2em;
font-family: monospace;
border-right: 2px solid white;
padding-right: 5px;
animation: blink-caret 0.7s step-end infinite;
white-space: nowrap;
overflow: hidden;
animation: typing 2s steps(20) forwards, blink-caret 0.7s step-end infinite;
}
@keyframes typing {
from { width: 0; }
to { width: 100%; }
}
@keyframes blink-caret {
50% { border-color: transparent; }
}
/* === 4. Bounce === */
.bounce {
width: 60px;
height: 60px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 12px;
margin: 0 auto;
animation: bounce 1s ease infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
/* === 5. Heartbeat Pulse === */
.heartbeat {
font-size: 3em;
animation: heartbeat 1.2s ease-in-out infinite;
display: inline-block;
}
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
14% { transform: scale(1.3); }
28% { transform: scale(1); }
42% { transform: scale(1.3); }
70% { transform: scale(1); }
}
/* === 6. Wave === */
.wave-container {
display: flex;
gap: 5px;
justify-content: center;
align-items: flex-end;
height: 50px;
}
.wave-bar {
width: 8px;
height: 20px;
background: #4ecdc4;
border-radius: 4px;
animation: wave 1.2s ease-in-out infinite;
}
.wave-bar:nth-child(1) { animation-delay: 0s; }
.wave-bar:nth-child(2) { animation-delay: 0.1s; }
.wave-bar:nth-child(3) { animation-delay: 0.2s; }
.wave-bar:nth-child(4) { animation-delay: 0.3s; }
.wave-bar:nth-child(5) { animation-delay: 0.4s; }
@keyframes wave {
0%, 100% { height: 20px; }
50% { height: 45px; }
}
/* === 7. Fade In === */
.fade-in {
animation: fadeIn 1.5s ease forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* === 8. Slide In === */
.slide-in-left {
animation: slideInLeft 0.6s ease forwards;
}
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-50px); }
to { opacity: 1; transform: translateX(0); }
}
/* === 9. Pulse Ring === */
.pulse-ring {
width: 60px;
height: 60px;
border-radius: 50%;
background: #667eea;
margin: 0 auto;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.pulse-ring::before,
.pulse-ring::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 2px solid #667eea;
animation: pulseRing 2s ease-out infinite;
}
.pulse-ring::after {
animation-delay: 0.5s;
}
@keyframes pulseRing {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(2); opacity: 0; }
}
/* === 10. Gradient Shift === */
.gradient-shift {
width: 200px;
height: 60px;
border-radius: 30px;
background: linear-gradient(90deg, #667eea, #764ba2, #f093fb, #f5576c, #667eea);
background-size: 300% 100%;
animation: gradientShift 3s ease infinite;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
}
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* === 11. Flip Card === */
.flip-container {
perspective: 1000px;
width: 120px;
height: 80px;
margin: 0 auto;
}
.flip-card {
width: 100%;
height: 100%;
position: relative;
animation: flipCard 2s ease-in-out infinite;
transform-style: preserve-3d;
}
.flip-card .front,
.flip-card .back {
position: absolute;
width: 100%;
height: 100%;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
backface-visibility: hidden;
font-weight: bold;
}
.flip-card .front {
background: #667eea;
color: white;
}
.flip-card .back {
background: #f5576c;
color: white;
transform: rotateY(180deg);
}
@keyframes flipCard {
0%, 100% { transform: rotateY(0deg); }
50% { transform: rotateY(180deg); }
}
/* === 12. Shake === */
.shake {
animation: shake 0.5s ease;
display: inline-block;
font-size: 2em;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-10px); }
40% { transform: translateX(10px); }
60% { transform: translateX(-5px); }
80% { transform: translateX(5px); }
}
</style>
</head>
<body>
<div class="demo-card">
<h3>Loading Spinner</h3>
<div class="spinner"></div>
</div>
<div class="demo-card">
<h3>Dual Ring Spinner</h3>
<div class="dual-ring"></div>
</div>
<div class="demo-card">
<h3>Typing Cursor</h3>
<div class="typing-container">
<span class="typing-text">Hello, welcome!</span>
</div>
</div>
<div class="demo-card">
<h3>Bounce</h3>
<div class="bounce"></div>
</div>
<div class="demo-card">
<h3>Heartbeat</h3>
<div class="heartbeat">❤️</div>
</div>
<div class="demo-card">
<h3>Wave</h3>
<div class="wave-container">
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
</div>
</div>
<div class="demo-card">
<h3>Gradient Shift</h3>
<div class="gradient-shift">Loading...</div>
</div>
<div class="demo-card">
<h3>Flip Card</h3>
<div class="flip-container">
<div class="flip-card">
<div class="front">FRONT</div>
<div class="back">BACK</div>
</div>
</div>
</div>
<div class="demo-card">
<h3>Pulse Ring</h3>
<div class="pulse-ring"></div>
</div>
</body>
</html>
Code Explanation
Loading Spinner: A border with only one colored side, rotating 360 degrees. border-radius: 50% creates the circle, and animation: spin 0.8s linear infinite rotates it continuously.
Typing Cursor: Uses overflow: hidden and an animated width from 0 to 100%. The steps() function creates the character-by-character typing effect. The border-right blinks as the cursor.
Bounce: The object moves up and down with translateY. The ease timing function gives it a natural feel.
Heartbeat: The scale changes in a specific pattern (small-big-small-big-small), mimicking a heartbeat. The delay between beats makes it feel natural.
Wave: Multiple bars with different animation-delay values create a wave effect. Each bar moves up and down with a time offset.
Gradient Shift: background-size: 300% gives the gradient room to move. background-position is animated to create the shifting effect.
Flip Card: transform-style: preserve-3d and backface-visibility: hidden enable the 3D flip effect. The two sides of the card are positioned differently with rotateY(180deg).
Pulse Ring: Pseudo-elements with scale from 1 to 2 and opacity from 0.8 to 0 create a spreading wave effect.
Everyday Use Cases
Loading states: A spinner while waiting for data to load from the server.
Empty states: A bounce or pulse animation on an empty-state illustration.
Call to action: A gradient shift on a CTA button to draw attention.
Notifications: A shake animation on a notification icon when a new message arrives.
Social media: A heartbeat on a like/love button.
Conclusion
Keyframe animations open up a wide range of visual possibilities. Common patterns: transform (translate, rotate, scale, skew), opacity (fade), and color/gradients. Use animation-delay for sequencing. Timing functions (ease, linear, ease-in-out, cubic-bezier) control the feel of the animation. Consider performance — avoid animating properties that trigger layout recalculation such as width, height, top, left.