transition only moved once there was a trigger like :hover. The @keyframes we learn this time is different. It can move automatically and repeatedly, from the moment the page opens.
A badge that gently floats on its own
Even without the cursor on it, it’s moving all by itself.
How to write @keyframes
<div class="badge">NEW</div>@keyframes float {
0% { transform: translateY(0); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0); }
}- The
floatafter@keyframesis the name (you can decide it however you like) 0%,50%, and100%are the timing of the motion (0% = start, 100% = end)- Inside each
{ }, you write the look at that timing
Since 0% is the start position you can shorten it to from, and since 100% is the end position you can write it as to. When there are only two points of timing, this form is often used too.
Load it with the animation property
Just writing the script won’t make anything move. You apply it to an element with animation.
<div class="badge">NEW</div>.badge {
animation: float 1.6s ease-in-out infinite;
}| Part | Role |
|---|---|
float | Which @keyframes to use (matched by name) |
1.6s | How long one loop takes |
ease-in-out | The rhythm of the motion (same idea as transition) |
infinite | How many times to repeat. infinite is an endless loop |
If you write a number like 3 instead of infinite, it moves just that many times and then stops.
Leave the round trip to alternate
The earlier float had you write a round-trip script yourself, 0% → 50% → 100%. Actually there’s also a way to write only the way out and let the browser handle the way back. That’s alternate.
<div class="badge">NEW</div>.badge {
animation: float 0.8s ease-in-out infinite alternate;
}
@keyframes float {
from { transform: translateY(0); }
to { transform: translateY(-10px); }
}When you add alternate, the first loop plays from → to, the second loop plays to → from in reverse, making a back-and-forth motion. The upside is the script takes half as much writing, and the way out and the way back are always symmetric (we’ve also halved the one-loop time to 0.8s).
Offset the start with animation-delay
When you give several elements the same motion, they all tend to move in unison and look mechanical. Offsetting the start with animation-delay makes it feel much more alive.
<div class="dot"></div>
<div class="dot dot--2"></div>
<div class="dot dot--3"></div>.dot {
animation: float 1s ease-in-out infinite alternate;
}
.dot--2 {
animation-delay: 0.2s;
}
.dot--3 {
animation-delay: 0.4s;
}That “loading” motion, where three dots bounce in a wave, is made with this time offset.
One more: a spinner that keeps rotating
<div class="spinner"></div>.spinner {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}For an effect like rotation that “repeats the same motion forever,” it’s standard to set the easing to linear (constant speed). With ease the speed changes each loop, so it looks like it stutters at the seam.
Summary of this lesson
- Make the motion’s script with
@keyframes name { 0% {...} 100% {...} } - Apply it to an element with
animation: name time easing count; infinitefor an endless loop,alternatefor a round trip,animation-delayfor a time offset- Use
@keyframesfor motion that repeats automatically,transitionfor motion that reacts