Keyframe animations for cards, icons, loaders and headings - drop a class on an element and it comes alive.
1Morphing Blob
Turns a shape or a photo frame into an organic blob that slowly changes its outline. Class: fx-morphing-blob
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<div class="fx-morphing-blob" style="width:150px;height:150px">
<img src="https://images.unsplash.com/photo-1578894381163-e72c17f2d45f?auto=format&fit=crop&w=800&h=800&q=70" alt="Product photo in a blob frame" class="">
</div>
<div class="fx-morphing-blob" style="width:90px;height:90px">
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-morphing-blob {
border-radius: 42% 58% 63% 37% / 41% 44% 56% 59%;
background: linear-gradient(135deg, var(--wt-primary), var(--wt-secondary));
overflow: hidden;
animation: fx-morphing-blob-morph 9s ease-in-out infinite;
}
.fx-morphing-blob img { width: 100%; height: 100%; object-fit: cover; display: block; }
@keyframes fx-morphing-blob-morph {
0%, 100% { border-radius: 42% 58% 63% 37% / 41% 44% 56% 59%; }
33% { border-radius: 70% 30% 46% 54% / 30% 39% 61% 70%; }
66% { border-radius: 30% 70% 70% 30% / 53% 30% 70% 47%; }
}
@media (prefers-reduced-motion: reduce) {
.fx-morphing-blob, .fx-morphing-blob::before, .fx-morphing-blob::after, .fx-morphing-blob *, .fx-morphing-blob *::before, .fx-morphing-blob *::after { animation: none !important; transition: none !important; }
}2Typewriter
Types a single line of monospace text letter by letter with a blinking caret; set --fx-ch to the character count. Class: fx-typewriter
terminal
Hello, world.<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<div class="bg-dark text-white rounded-3 p-3 w-100" style="max-width:18rem">
<div class="small text-white-50 mb-1">
terminal
</div>
<span class="fx-typewriter" style="--fx-ch:13">Hello, world.</span>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-typewriter {
display: inline-block; max-width: 100%; overflow: hidden; white-space: nowrap; vertical-align: bottom;
font-family: var(--ui-font-monospace, monospace);
width: calc(var(--fx-ch, 20) * 1ch + .15em);
border-right: .12em solid var(--wt-primary);
animation: fx-typewriter-type 4.5s steps(var(--fx-ch, 20)) infinite, fx-typewriter-caret .8s step-end infinite;
}
@keyframes fx-typewriter-type { 0% { width: 0; } 65%, 100% { width: calc(var(--fx-ch, 20) * 1ch + .15em); } }
@keyframes fx-typewriter-caret { 50% { border-color: transparent; } }
@media (prefers-reduced-motion: reduce) {
.fx-typewriter, .fx-typewriter::before, .fx-typewriter::after, .fx-typewriter *, .fx-typewriter *::before, .fx-typewriter *::after { animation: none !important; transition: none !important; }
}