Polished hover and focus transitions for buttons, cards and links that work on any .btn or .card.
1Neon Fill Button
An outline button that fills with colour from the left and glows; works on any .btn or link. Class: fx-neon-fill-button
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<button type="button" class="btn btn-lg fx-neon-fill-button">Join now</button>
<a href="#" class="btn fx-neon-fill-button rounded-full">Learn more <i class="bi bi-arrow-right" aria-hidden="true"></i></a>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-neon-fill-button, .btn.fx-neon-fill-button {
position: relative; isolation: isolate; overflow: hidden; z-index: 0;
color: var(--wt-primary-ink); background: transparent; border: 2px solid var(--wt-primary);
transition: color .35s ease, box-shadow .35s ease;
}
.fx-neon-fill-button::before {
content: ""; position: absolute; inset: 0; z-index: -1; background: var(--wt-primary);
transform: scaleX(0); transform-origin: left center; transition: transform .4s cubic-bezier(.7, 0, .3, 1);
}
.fx-neon-fill-button:hover, .fx-neon-fill-button:focus-visible, .btn.fx-neon-fill-button:hover, .btn.fx-neon-fill-button:focus-visible {
color: var(--wt-on-primary, #fff); border-color: var(--wt-primary);
box-shadow: 0 0 12px rgba(var(--wt-primary-rgb), .6), 0 0 32px rgba(var(--wt-primary-rgb), .35);
}
.fx-neon-fill-button:hover::before, .fx-neon-fill-button:focus-visible::before { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
.fx-neon-fill-button, .fx-neon-fill-button::before, .fx-neon-fill-button::after, .fx-neon-fill-button *, .fx-neon-fill-button *::before, .fx-neon-fill-button *::after { animation: none !important; transition: none !important; }
}2Liquid Ripple
A button that fills with a swirling liquid from the bottom on hover. Class: fx-liquid-ripple
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<button type="button" class="btn btn-lg px-6 fx-liquid-ripple">Subscribe</button>
<button type="button" class="btn rounded-full fx-liquid-ripple">Hover me</button>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-liquid-ripple, .btn.fx-liquid-ripple {
position: relative; isolation: isolate; overflow: hidden; z-index: 0;
color: var(--wt-primary-ink); background: var(--wt-surface, #fff); border: 2px solid var(--wt-primary);
transition: color .5s ease;
}
.fx-liquid-ripple::before {
content: ""; position: absolute; z-index: -1; left: 50%; top: 100%; width: 240%; aspect-ratio: 1;
margin-left: -120%; margin-top: 12%; border-radius: 42%;
background: var(--wt-primary);
transition: top .8s ease, margin-top .8s ease;
animation: fx-liquid-ripple-swirl 5s linear infinite;
}
.fx-liquid-ripple:hover, .fx-liquid-ripple:focus-visible, .btn.fx-liquid-ripple:hover, .btn.fx-liquid-ripple:focus-visible { color: var(--wt-on-primary, #fff); border-color: var(--wt-primary); }
.fx-liquid-ripple:hover::before, .fx-liquid-ripple:focus-visible::before { top: 50%; margin-top: -120%; }
.fx-liquid-ripple:active { transform: scale(.97); }
@keyframes fx-liquid-ripple-swirl { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
.fx-liquid-ripple, .fx-liquid-ripple::before, .fx-liquid-ripple::after, .fx-liquid-ripple *, .fx-liquid-ripple *::before, .fx-liquid-ripple *::after { animation: none !important; transition: none !important; }
}3Elastic Scale
Buttons, chips and cards that grow with a rubbery overshoot on hover and squash when pressed. Class: fx-elastic-scale
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<button type="button" class="btn btn-primary fx-elastic-scale">Save</button>
<span class="badge rounded-full text-bg-secondary text-base fx-elastic-scale">how-to</span> <a href="#" class="btn btn-outline-primary rounded-full text-lg fx-elastic-scale" aria-label="Cart, $48.39"><i class="bi bi-cart3" aria-hidden="true"></i></a>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-elastic-scale { transition: transform .5s cubic-bezier(.34, 1.56, .64, 1); }
.fx-elastic-scale:hover, .fx-elastic-scale:focus-visible { transform: scale(1.08); }
.fx-elastic-scale:active { transform: scale(.94); transition-duration: .15s; }
@media (prefers-reduced-motion: reduce) {
.fx-elastic-scale, .fx-elastic-scale::before, .fx-elastic-scale::after, .fx-elastic-scale *, .fx-elastic-scale *::before, .fx-elastic-scale *::after { animation: none !important; transition: none !important; }
}