Modern CSS Transitions
Polished hover and focus transitions for buttons, cards and links that work on any .btn or .card.
1Rainbow Glow
Surrounds a button or card with a glow that cycles through the theme colours while hovered. Class: fx-rainbow-glow
Pro plan
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<button type="button" class="btn btn-dark btn-lg rounded-pill fx-rainbow-glow">Get the bundle</button>
<div class="card fx-rainbow-glow" style="width:9rem" tabindex="0">
<div class="card-body text-center py-3">
<div class="fs-3 text-primary">
<i class="bi bi-gem" aria-hidden="true"></i>
</div>
<div class="small fw-semibold">
Pro plan
</div>
</div>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-rainbow-glow { transition: box-shadow .4s ease, transform .4s ease; }
.fx-rainbow-glow:hover, .fx-rainbow-glow:focus-visible { transform: translateY(-2px); animation: fx-rainbow-glow-glow 2.4s linear infinite; }
@keyframes fx-rainbow-glow-glow {
0%, 100% { box-shadow: 0 0 14px rgba(var(--wt-primary-rgb), .75), 0 0 34px rgba(var(--wt-primary-rgb), .35); }
33% { box-shadow: 0 0 14px rgba(var(--wt-accent-rgb), .75), 0 0 34px rgba(var(--wt-accent-rgb), .35); }
66% { box-shadow: 0 0 14px rgba(var(--wt-secondary-rgb), .75), 0 0 34px rgba(var(--wt-secondary-rgb), .35); }
}
@media (prefers-reduced-motion: reduce) {
.fx-rainbow-glow, .fx-rainbow-glow::before, .fx-rainbow-glow::after, .fx-rainbow-glow *, .fx-rainbow-glow *::before, .fx-rainbow-glow *::after { animation: none !important; transition: none !important; }
}2Split Text Fill
Fills a heading or link with the primary colour from both sides at once on hover - top half from the left, bottom half from the right. Class: fx-split-text-fill
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<a href="#" class="display-6 fw-bold fx-split-text-fill">Hover this title</a>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-split-text-fill {
display: inline-block; text-decoration: none;
-webkit-text-fill-color: transparent;
background-image: linear-gradient(var(--wt-primary), var(--wt-primary)), linear-gradient(var(--wt-primary), var(--wt-primary)), linear-gradient(currentColor, currentColor);
background-repeat: no-repeat;
background-position: 0 0, 100% 100%, 0 0;
background-size: 0% 50%, 0% 50%, 100% 100%;
-webkit-background-clip: text; background-clip: text;
transition: background-size .6s cubic-bezier(.7, 0, .3, 1);
}
.fx-split-text-fill:hover, .fx-split-text-fill:focus-visible { background-size: 100% 50%, 100% 50%, 100% 100%; }
@supports not ((background-clip: text) or (-webkit-background-clip: text)) {
.fx-split-text-fill { -webkit-text-fill-color: currentColor; background: none; }
.fx-split-text-fill:hover { color: var(--wt-primary-ink); }
}
@media (prefers-reduced-motion: reduce) {
.fx-split-text-fill, .fx-split-text-fill::before, .fx-split-text-fill::after, .fx-split-text-fill *, .fx-split-text-fill *::before, .fx-split-text-fill *::after { animation: none !important; transition: none !important; }
}