CSS
CSS Animations
Master keyframe animations to bring your websites to life with smooth, professional motion effects.
CSS animations make websites feel alive. Think of animations like a flipbook — you define keyframes (the important pictures) and CSS smoothly fills in all the frames between them. The WanderLust team wants their hero section to have a gentle fade-in effect and their destination cards to bounce playfully when visitors hover over them. Animations differ from transitions because they can run automatically, loop forever, and include multiple steps. Whiletransition only goes from point A to point B, animation can go A → B → C → D and back to A in a continuous loop.
Creating Keyframes
Keyframes are the building blocks of CSS animations. You define what an element looks like at different points in time, and CSS creates smooth motion between those points. Think of it like choreographing a dance — you set the key poses, and the dancer fills in the graceful movements between them./* Define animation keyframes for WanderLust hero fade-in */
@keyframes fadeIn {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}What just happened?
The @keyframes rule creates a blueprint for animation. At 0% (start), the hero is invisible and 20px below its final position. At 100% (end), it's fully visible and in place. Try refreshing to see the animation again.
from keyword equals 0%, and to equals 100%. For more complex animations, use specific percentage values to control timing precisely.
/* Multi-step animation for WanderLust destination card */
@keyframes bounce {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.destination-card {
animation: bounce 0.6s ease-in-out;
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}Animation Properties
Theanimation property is actually shorthand for eight individual properties. Understanding each one gives you precise control over how your animations behave. Think of these properties like settings on a camera — each controls a different aspect of the final result.
/* Individual animation properties for WanderLust loading spinner */
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e2e8f0;
border-top: 4px solid #0ea5e9;
border-radius: 50%;
animation-name: spin; /* Which @keyframes to use */
animation-duration: 1s; /* How long one cycle takes */
animation-timing-function: linear; /* Speed curve */
animation-delay: 0s; /* Wait before starting */
animation-iteration-count: infinite; /* How many times */
animation-direction: normal; /* Forward, reverse, alternate */
animation-fill-mode: none; /* What styles apply when stopped */
animation-play-state: running; /* Running or paused */
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}name duration timing-function delay iteration-count direction fill-mode. You can skip properties you don't need — CSS will use sensible defaults.
Timing Functions
Timing functions control the speed curve of your animation — how it accelerates and decelerates. Real objects don't move at constant speeds. A ball bounces fast then slows at the peak, a car accelerates from a stop. CSS timing functions make animations feel natural instead of robotic./* Different timing functions for WanderLust button animations */
.button-ease { animation: slideIn 0.8s ease; }
.button-linear { animation: slideIn 0.8s linear; }
.button-custom { animation: slideIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55); }
@keyframes slideIn {
from { transform: translateX(-100px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}Pro Tip
Use cubic-bezier.com to visually create custom timing functions. The bounce effect above uses control points that go outside the 0-1 range, creating overshoot that snaps back.
Animation Iteration and Direction
Control how many times animations repeat and in which direction they play. The WanderLust team wants their promotional badges to pulse continuously and their image carousel to alternate direction each cycle for a smooth back-and-forth effect./* Pulsing "New Destination" badge for WanderLust */
.badge-pulse {
background: #f97316;
color: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.8; }
}animation-direction property changes the flow of your animation. alternate plays forward then reverse, creating smooth loops without jarring resets. reverse plays the keyframes backwards.
/* Alternating slide animation for WanderLust testimonial */
.testimonial {
background: #f8fafc;
padding: 20px;
border-radius: 12px;
border-left: 4px solid #0ea5e9;
animation: slideAlternate 4s ease-in-out infinite alternate;
}
@keyframes slideAlternate {
from { transform: translateX(0); }
to { transform: translateX(20px); }
}Animation Fill Modes and States
Fill modes control what happens before and after your animation runs. Think of it like a movie —fill-mode decides whether you see the first frame, last frame, or original state when the movie isn't playing. The play-state property lets you pause and resume animations dynamically.
/* WanderLust modal with controlled animation state */
.modal {
background: white;
border-radius: 16px;
padding: 32px;
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
animation: modalEnter 0.5s ease-out forwards;
animation-fill-mode: both; /* Apply first keyframe immediately */
}
.modal.paused {
animation-play-state: paused; /* Freeze animation */
}
@keyframes modalEnter {
from {
opacity: 0;
transform: scale(0.8) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}What just happened?
The forwards fill-mode keeps the modal at its final animated state instead of snapping back to the original styles. This prevents the jarring jump that would happen when the animation completes.
Performance and Best Practices
Not all CSS properties animate smoothly. Browsers can optimizetransform and opacity animations by using the GPU (graphics processing unit), making them buttery smooth even on slower devices. Animating properties like width, height, or top forces the browser to recalculate layout every frame, causing stutters.
Performance Champions
transform: scale(), rotate(), translate() — GPU accelerated, 60fps smooth
opacity: fade in/out — GPU accelerated, perfect for loading states
filter: blur(), brightness() — Modern browsers optimize these well
Avoid Animating
width, height: Causes layout recalculation every frame
top, left, margin: Forces expensive position recalculations
box-shadow: Complex shadows can be costly — use sparingly
will-change: transform to hint to browsers which elements will be animated, but remove it when animation completes to free up memory.
/* Performance-optimized WanderLust card hover animation */
.destination-card {
will-change: transform; /* Hint to browser */
transition: transform 0.3s ease;
}
.destination-card:hover {
transform: translateY(-8px) scale(1.02); /* GPU-optimized */
}
.destination-card:not(:hover) {
will-change: auto; /* Free resources when idle */
}Quiz
1. WanderLust wants a "Book Now" button that grows by 20% at the halfway point, then returns to normal size. Which keyframe definition creates this effect?
2. A WanderLust loading spinner needs to rotate continuously at constant speed, taking 2 seconds per rotation. Which animation shorthand achieves this?
3. For the smoothest 60fps animations on all devices, WanderLust should primarily animate which CSS properties?
Up Next: Transitions and Transforms
Master hover effects and smooth property changes that respond to user interaction.