CSS course menu
CSS Keyframe Animations: @keyframes, animation Properties and Examples
Create CSS animations with @keyframes: animation-name, duration, timing function, delay, iteration count, direction, fill mode and play state, the animation shorthand, loading spinners and pulse effects, and accessible motion.
On this page
A transition animates between two states when something changes. A keyframe animation can run on its own, loop forever and pass through any number of steps.
@keyframes#
First, describe the animation with @keyframes and a name. Then attach it to an element with animation:
@keyframes slide-in {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.toast {
display: inline-block;
padding: 10px 16px;
background: #2f9e44;
color: white;
border-radius: 8px;
animation: slide-in 0.6s ease-out;
}<div class="toast">Saved successfully</div>The animation runs once, when the frame loads. Press Replay to run it again.
from and to are the same as 0% and 100%. You can add as many steps as you like:
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-40px); }
}
.ball {
width: 40px;
height: 40px;
margin-top: 50px;
border-radius: 50%;
background: #e8590c;
animation: bounce 1s ease-in-out infinite;
}<div class="ball"></div>The animation properties#
The shorthand: animation: name duration timing-function delay iteration-count direction fill-mode;. The first time value is the duration and the second the delay.
fill-mode#
By default, once an animation ends, the element snaps back to its normal styles. animation-fill-mode: forwards keeps the final keyframe's styles; backwards applies the first keyframe during the delay; both does both.
Practical examples#
A loading spinner#
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
width: 36px;
height: 36px;
border: 4px solid #dbe4ff;
border-top-color: #2f5fe0;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}<div class="spinner" role="status" aria-label="Loading"></div>A spinner is only visual, so role="status" and a label tell screen reader users that something is loading.
A pulse that draws attention#
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgb(232 89 12 / 0.6); }
100% { box-shadow: 0 0 0 16px rgb(232 89 12 / 0); }
}
.live {
display: inline-block;
margin: 16px;
width: 14px;
height: 14px;
border-radius: 50%;
background: #e8590c;
animation: pulse 1.5s ease-out infinite;
}<span class="live"></span> Live nowStaggered entrances#
A different animation-delay on each item makes them appear one after another:
@keyframes rise { from { opacity: 0; transform: translateY(12px); } }
li { animation: rise 0.5s ease-out both; }
li:nth-child(2) { animation-delay: 0.15s; }
li:nth-child(3) { animation-delay: 0.3s; }<ul>
<li>Learn HTML</li>
<li>Learn CSS</li>
<li>Learn JavaScript</li>
</ul>A keyframe with only from animates from those values to the element's normal styles.
Pausing on hover#
.marquee { animation: scroll 10s linear infinite; }
.marquee:hover { animation-play-state: paused; }Accessibility#
- Anything that moves, blinks or scrolls automatically for more than 5 seconds should have a way to pause or stop it (WCAG 2.2.2).
- Never flash content more than three times per second; flashing can trigger seizures.
- Turn off non-essential animation for users who ask for reduced motion:
@media (prefers-reduced-motion: reduce) {
.ball, .live { animation: none; }
}Practice#
Practice exercise
Create a "typing" indicator: three grey dots that each fade between 30% and 100% opacity in a 1.2s infinite loop, with the second and third dots delayed by 0.2s and 0.4s.
Show solution
@keyframes blink {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.typing { display: inline-flex; gap: 5px; padding: 10px 14px; background: #f1f3f5; border-radius: 16px; }
.typing span { width: 8px; height: 8px; border-radius: 50%; background: #666; animation: blink 1.2s infinite; }
.typing span:nth-child(2) { animation-delay: 0.2s; }
.typing span:nth-child(3) { animation-delay: 0.4s; }<div class="typing" role="status" aria-label="Asha is typing"><span></span><span></span><span></span></div>Key takeaway#
Finished this lesson?