Modern CSS Transitions
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="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-lg fx-neon-fill-button">Join now</button>
<a href="#" class="btn fx-neon-fill-button rounded-pill">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; }
}2Rainbow 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; }
}3Split 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">Read the story</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; }
}