Day 14: Keyframe Animations
✅ Today you will learn
- @keyframes with from/to and percentages
- animation-name, duration, timing, delay, iteration-count, direction, fill-mode
- Loaders: spinner, dots and skeleton screens
- Attention effects: pulse, shake, marquee offer strip
- Staggered entrances and respecting prefers-reduced-motion
🏢 In the real world
On slow 4G connections, users stare at a blank screen while data loads and think the app is broken. Apps like Swiggy and Flipkart show grey “skeleton” boxes and spinners instead – it feels faster even though it is not. Today you build those loaders plus the festive effects marketing teams always ask for.
@keyframes + animation
<style>
@keyframes slide-in {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 0 0 rgb(245 158 11 / 60%); }
50% { box-shadow: 0 0 0 14px rgb(245 158 11 / 0%); }
}
.banner { background: #264de4; color: #fff; padding: 12px 16px; border-radius: 10px;
animation: slide-in .7s ease-out both; }
.offer { display: inline-block; margin-top: 16px; background: #f59e0b; padding: 8px 14px; border-radius: 99px; font-weight: 700;
animation: glow 1.8s ease-in-out infinite; }
</style>
<div class="banner">Welcome to Sharma Sweets 🍬</div>
<span class="offer">🪔 Diwali offer – 10% off</span>
<p><button onclick="var b=document.querySelector('.banner'); b.style.animation='none'; b.offsetWidth; b.style.animation='';">Replay</button></p>| Property | Example | Meaning |
|---|---|---|
| animation-name | slide-in | Which @keyframes |
| animation-duration | .7s | One cycle |
| animation-timing-function | ease-out, steps(4) | Speed curve |
| animation-delay | .2s | Start later |
| animation-iteration-count | 1, 3, infinite | How many times |
| animation-direction | alternate | Play forward then backward |
| animation-fill-mode | both / forwards | Keep the first/last frame before/after |
| animation-play-state | paused | Pause, e.g. on hover |
Loaders and skeleton screens
<style>
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } }
@keyframes shimmer { to { background-position: -200% 0; } }
.spinner { width: 40px; height: 40px; border: 4px solid #dbeafe; border-top-color: #264de4; border-radius: 50%; animation: spin .8s linear infinite; }
.dots span { display: inline-block; width: 12px; height: 12px; margin: 0 3px; background: #264de4; border-radius: 50%; animation: bounce 1.2s infinite ease-in-out both; }
.dots span:nth-child(1) { animation-delay: -.32s; }
.dots span:nth-child(2) { animation-delay: -.16s; }
.sk { background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%); background-size: 200% 100%; animation: shimmer 1.4s linear infinite; border-radius: 8px; }
.sk-card { display: flex; gap: 12px; align-items: center; margin-top: 16px; }
.sk-img { width: 70px; height: 70px; }
.sk-line { height: 12px; margin: 8px 0; }
.row { display: flex; gap: 40px; align-items: center; }
</style>
<div class="row">
<div class="spinner" role="status" aria-label="Loading"></div>
<div class="dots" aria-hidden="true"><span></span><span></span><span></span></div>
</div>
<div class="sk-card" aria-busy="true">
<div class="sk sk-img"></div>
<div style="flex:1"><div class="sk sk-line" style="width:70%"></div><div class="sk sk-line" style="width:45%"></div></div>
</div>Give spinners role="status" and an aria-label (or visually hidden text “Loading…”), and set aria-busy="true" on the area being loaded so screen-reader users know something is happening.
Attention effects that sell
<style>
@keyframes shake { 10%, 90% { transform: translateX(-1px); } 20%, 80% { transform: translateX(2px); } 30%, 50%, 70% { transform: translateX(-4px); } 40%, 60% { transform: translateX(4px); } }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes ring { 0%, 100% { transform: rotate(0); } 10%, 30% { transform: rotate(-15deg); } 20%, 40% { transform: rotate(15deg); } 50% { transform: rotate(0); } }
.error { border: 2px solid #dc2626; padding: 8px; border-radius: 8px; width: 220px; animation: shake .5s; }
.strip { overflow: hidden; background: #111827; color: #fde68a; border-radius: 8px; margin: 14px 0; }
.strip .track { display: flex; width: max-content; animation: marquee 12s linear infinite; }
.strip span { padding: 8px 24px; white-space: nowrap; }
.strip:hover .track { animation-play-state: paused; }
.bell { display: inline-block; font-size: 28px; animation: ring 2s ease-in-out infinite; transform-origin: top center; }
</style>
<input class="error" value="98290" aria-invalid="true"> <small style="color:#dc2626">Enter 10 digits</small>
<div class="strip"><div class="track">
<span>🪔 Diwali gift boxes from ₹480</span><span>🚚 Free delivery above ₹1,500</span><span>🍬 Sugar-free kaju katli launched</span>
<span aria-hidden="true">🪔 Diwali gift boxes from ₹480</span><span aria-hidden="true">🚚 Free delivery above ₹1,500</span><span aria-hidden="true">🍬 Sugar-free kaju katli launched</span>
</div></div>
<span class="bell" aria-hidden="true">🔔</span> 3 new ordersStaggered entrance
<style>
@keyframes rise { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
.list li { list-style: none; background: #eef2ff; margin: 6px 0; padding: 10px 14px; border-radius: 8px;
animation: rise .5s ease-out both;
animation-delay: calc(var(--i) * 120ms); } /* each item waits a bit longer */
</style>
<ul class="list" style="padding:0">
<li style="--i:0">✔ Order received</li>
<li style="--i:1">✔ Sweets packed</li>
<li style="--i:2">✔ Out for delivery</li>
<li style="--i:3">✔ Delivered</li>
</ul>Respect prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
transition-duration: .01ms !important;
scroll-behavior: auto !important;
}
}People with vestibular disorders can feel dizzy or nauseous from large moving elements. Phones and computers have a “reduce motion” setting; this snippet honours it. It is one of the rare good uses of !important.
🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Show solution
Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
How do you make loading feel faster?
Show skeleton screens matching the final layout, use lightweight CSS spinners only for short waits, load content progressively, reserve space to avoid layout shifts, and give accessible status messages.
What is animation-fill-mode?
It decides which styles apply before the animation starts (backwards – first keyframe during the delay) and after it ends (forwards – last keyframe stays). both does both.
Summary
- @keyframes defines steps; animation applies them.
- infinite + linear for spinners; both/forwards to keep frames.
- Skeletons and spinners improve perceived speed.
- Always add the prefers-reduced-motion snippet.
Day 15: styling forms and tables – inputs, custom checkboxes, validation states and responsive tables.
