Modern CSS Animations
Keyframe animations for cards, icons, loaders and headings - drop a class on an element and it comes alive.
1Bouncing Text
Makes each letter of a short word hop in turn; wrap every letter in a span with --i set to its index. Class: fx-bouncing-text
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<h2 class="display-5 font-bold mb-0 fx-bouncing-text" aria-label="Launch"><span style="--i:0" aria-hidden="true">L</span><span style="--i:1" aria-hidden="true">a</span><span style="--i:2" aria-hidden="true">u</span><span style="--i:3" aria-hidden="true">n</span><span style="--i:4" aria-hidden="true">c</span><span style="--i:5" aria-hidden="true">h</span></h2>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-bouncing-text { display: inline-flex; }
.fx-bouncing-text > span {
display: inline-block;
animation: fx-bouncing-text-bounce 1.6s ease-in-out infinite;
animation-delay: calc(var(--i, 0) * .09s);
}
@keyframes fx-bouncing-text-bounce {
0%, 60%, 100% { transform: translateY(0); }
25% { transform: translateY(-14px); color: var(--wt-primary-ink); }
40% { transform: translateY(2px); }
}
@media (prefers-reduced-motion: reduce) {
.fx-bouncing-text, .fx-bouncing-text::before, .fx-bouncing-text::after, .fx-bouncing-text *, .fx-bouncing-text *::before, .fx-bouncing-text *::after { animation: none !important; transition: none !important; }
}2Gradient Wave
A banner whose theme gradient flows sideways while a soft wave rolls along its bottom edge. Class: fx-gradient-wave
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<div class="fx-gradient-wave rounded-xl p-6 w-full" style="max-width:19rem">
<p class="small uppercase font-semibold mb-1 opacity-75">New season</p>
<h3 class="h4 font-bold mb-4">Meet the people shaping the future</h3>
<a href="#" class="btn btn-light btn-sm">Explore</a>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-gradient-wave {
position: relative; overflow: hidden; color: var(--wt-on-primary, #fff);
background: linear-gradient(90deg, var(--wt-primary), var(--wt-secondary), var(--wt-accent), var(--wt-primary));
background-size: 200% 100%;
animation: fx-gradient-wave-flow 8s linear infinite;
}
.fx-gradient-wave::after {
content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 24px; pointer-events: none;
background: radial-gradient(20px 12px at 50% 100%, rgba(255, 255, 255, .35) 98%, transparent 100%) 0 100% / 40px 24px repeat-x;
animation: fx-gradient-wave-roll 2.4s linear infinite;
}
.fx-gradient-wave > * { position: relative; z-index: 1; }
@keyframes fx-gradient-wave-flow { to { background-position: -200% 0; } }
@keyframes fx-gradient-wave-roll { to { background-position: 40px 100%; } }
@media (prefers-reduced-motion: reduce) {
.fx-gradient-wave, .fx-gradient-wave::before, .fx-gradient-wave::after, .fx-gradient-wave *, .fx-gradient-wave *::before, .fx-gradient-wave *::after { animation: none !important; transition: none !important; }
}3Heartbeat
A double-beat pulse for like buttons, favourite icons and notification badges. Class: fx-heartbeat
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<span class="display-4 text-danger-emphasis fx-heartbeat"><i class="bi bi-heart-fill" aria-hidden="true"></i></span>
<button type="button" class="btn btn-outline-danger rounded-full"><span class="fx-heartbeat"><i class="bi bi-heart-fill" aria-hidden="true"></i></span> 522 likes</button>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-heartbeat { display: inline-block; animation: fx-heartbeat-beat 1.5s ease-in-out infinite; transform-origin: center; }
@keyframes fx-heartbeat-beat {
0%, 40%, 100% { transform: scale(1); }
12% { transform: scale(1.2); }
24% { transform: scale(.96); }
32% { transform: scale(1.14); }
}
@media (prefers-reduced-motion: reduce) {
.fx-heartbeat, .fx-heartbeat::before, .fx-heartbeat::after, .fx-heartbeat *, .fx-heartbeat *::before, .fx-heartbeat *::after { animation: none !important; transition: none !important; }
}