.tt32 {
background: #1c1424;
padding: 36px 18px;
font-family: ui-sans-serif, system-ui, sans-serif;
min-height: 220px;
box-sizing: border-box;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.tt32n {
position: relative;
display: flex;
gap: 0;
min-width: 0;
width: 100%;
max-width: 420px;
}
.tt32line {
position: absolute;
bottom: -2px;
left: 0;
height: 2px;
width: 0;
background: #ff6b6b;
border-radius: 2px;
transition:
left 0.4s cubic-bezier(0.65, 0, 0.35, 1),
width 0.4s cubic-bezier(0.65, 0, 0.35, 1);
pointer-events: none;
}
.tt32b {
flex: 1;
min-width: 0;
padding: 10px 12px;
border: 0;
background: transparent;
font:
600 12px/1 ui-sans-serif,
system-ui;
letter-spacing: 0.04em;
color: rgba(255, 255, 255, 0.55);
cursor: pointer;
position: relative;
transition: color 0.25s;
white-space: nowrap;
}
.tt32b:hover {
color: rgba(255, 255, 255, 0.9);
}
.tt32b.active {
color: #ff6b6b;
}
.tt32b:focus-visible {
outline: 2px solid #ff6b6b;
outline-offset: 2px;
border-radius: 4px;
}
.tt32spark {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
pointer-events: none;
animation: tt32-fly 0.6s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}
@keyframes tt32-fly {
from {
transform: translate(0, 0) scale(1);
opacity: 1;
}
to {
transform: translate(var(--dx), var(--dy)) scale(0.3);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.tt32line {
transition: none;
}
.tt32spark {
animation: none;
opacity: 0;
}
} <div class="tt32">
<nav class="tt32n">
<span class="tt32line" aria-hidden="true"></span>
<button class="tt32b active" data-t>Spark</button>
<button class="tt32b" data-t>Bloom</button>
<button class="tt32b" data-t>Burst</button>
<button class="tt32b" data-t>Flare</button>
</nav>
</div> /* Particle Burst — toggle .active, slide underline, spawn 8 color sparks
on click. Re-positions the underline on viewport resize. */
(function () {
var nav = document.querySelector(".tt32n");
if (!nav) return;
var btns = nav.querySelectorAll("[data-t]");
var line = nav.querySelector(".tt32line");
var current = null;
var COLORS = ["#ff6b6b", "#ffd166", "#06d6a0", "#118ab2", "#ef476f"];
function reposition() {
if (!current || !line) return;
line.style.left = current.offsetLeft + "px";
line.style.width = current.offsetWidth + "px";
}
function burst(btn) {
var rect = btn.getBoundingClientRect();
var navRect = nav.getBoundingClientRect();
var cx = rect.left - navRect.left + rect.width / 2;
var cy = rect.top - navRect.top + rect.height / 2;
for (var i = 0; i < 8; i++) {
var s = document.createElement("span");
s.className = "tt32spark";
s.style.left = cx + "px";
s.style.top = cy + "px";
s.style.background = COLORS[i % COLORS.length];
var angle = (i / 8) * Math.PI * 2;
var dist = 32 + Math.random() * 18;
s.style.setProperty("--dx", Math.cos(angle) * dist + "px");
s.style.setProperty("--dy", Math.sin(angle) * dist + "px");
nav.appendChild(s);
setTimeout(
(function (n) {
return function () {
if (n.parentNode) n.parentNode.removeChild(n);
};
})(s),
700,
);
}
}
function activate(btn, withBurst) {
current = btn;
btns.forEach(function (b) {
b.classList.toggle("active", b === btn);
});
reposition();
if (withBurst) burst(btn);
}
btns.forEach(function (b) {
b.addEventListener("click", function () {
activate(b, true);
});
});
window.addEventListener("resize", reposition);
var initial = nav.querySelector("[data-t].active") || btns[0];
if (initial) activate(initial, false);
})(); Live preview Edit any tab — preview updates live Ready