TaskBuild a pulse animation. @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }. .heart { font-size: 48px; animation: pulse 1.5s ease-in-out infinite; display: inline-block; }. Show a ❤ emoji that pulses.
@keyframes: declaring multi-step animations
75 XP6 min
Theory
Two ways to write keyframes
/* from/to (= 0%/100%) — simple two-state */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* percentages — multi-step */
@keyframes bounce {
0% { transform: translateY(0); }
40% { transform: translateY(-30px); }
70% { transform: translateY(-10px); }
100% { transform: translateY(0); }
}Then apply with the animation shorthand:
.fade-in { animation: fade-in 0.5s ease-out forwards; }
.bounce { animation: bounce 1s ease-in-out infinite; }The shorthand: animation: name dur timing iter direction fill
animation: bounce 1s ease-in-out 2 alternate forwards;
Reads: "bounce, 1s long, ease-in-out curve, 2 iterations, alternate direction, hold final keyframe."
iteration-count—infiniteor a number.direction—normal,reverse,alternate(forward then back),alternate-reverse.fill-mode—forwards(hold final),backwards(apply initial before start),both,none(default).
When to use animation-fill-mode: forwards
Without forwards, the animation runs and then SNAPS back to the pre-animation state. Always add forwards for one-shot entrance animations.
Don't forget reduced-motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}(Recap from A5 lesson 6.) Kill all motion for users who opted out.
🔒
Sign up to start coding
Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial — card required, cancel anytime.
Sign up — free trial →First 10 lessons in each track are free. No card needed for those.