Kinetic Trio
Three icon actions where the active button morphs into a confirmation pill, expands in width, and plays a gradient flow before snapping back. Layout shift handled with CSS grid auto-flow.
Kinetic Trio the 19th of 22 designs in the 22 CSS Button Group Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<div class="cbgp-kinetic" role="group" aria-label="Quick actions">
<button type="button" data-cbgp-kinetic-flash="✓ Copied" aria-label="Copy link">
<svg viewBox="0 0 24 24" aria-hidden="true">
<rect x="9" y="9" width="13" height="13" rx="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
<span>Copy</span>
</button>
<button type="button" data-cbgp-kinetic-flash="↗ Sharing" aria-label="Share">
<svg viewBox="0 0 24 24" aria-hidden="true">
<circle cx="18" cy="5" r="3" />
<circle cx="6" cy="12" r="3" />
<circle cx="18" cy="19" r="3" />
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
</svg>
<span>Share</span>
</button>
<button type="button" data-cbgp-kinetic-flash="↓ Downloading" aria-label="Download">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3" />
</svg>
<span>Download</span>
</button>
</div> .cbgp-kinetic {
display: inline-flex; gap: 6px;
padding: 5px;
background: linear-gradient(135deg, #0a0a14 0%, #14141d 100%);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 10px;
}
.cbgp-kinetic button {
display: inline-flex; align-items: center; gap: 7px;
padding: 9px 16px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 8px;
color: rgba(220,225,230,0.85);
font: 600 12px/1 ui-monospace, monospace;
letter-spacing: 0.08em;
cursor: pointer;
transition: all 0.4s cubic-bezier(.34,1.56,.64,1);
white-space: nowrap;
}
.cbgp-kinetic button:hover {
background: rgba(0,255,224,0.06);
border-color: rgba(0,255,224,0.4);
color: #fff;
}
.cbgp-kinetic svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.4s cubic-bezier(.34,1.56,.64,1); }
.cbgp-kinetic button.is-flash {
background:
linear-gradient(90deg, #00ffe0, #ff5af1) 0% 0% / 200% 100%;
border-color: transparent;
color: #001f1c;
animation: cbgp-kinetic-shift 1.4s linear;
text-shadow: 0 1px 0 rgba(255,255,255,0.4);
}
.cbgp-kinetic button.is-flash svg { transform: scale(1.2); }
@keyframes cbgp-kinetic-shift {
from { background-position: 200% 0%; }
to { background-position: 0% 0%; }
}
.cbgp-kinetic button:focus-visible { outline: 2px solid #00ffe0; outline-offset: 3px; } /* Kinetic action buttons — morphing flash on click. */
document.querySelectorAll('[data-cbgp-kinetic-flash]').forEach(function (btn) {
var label = btn.querySelector('span');
var msg = btn.dataset.cbgpKineticFlash;
var orig = label ? label.textContent : '';
btn.addEventListener('click', function () {
if (btn.classList.contains('is-flash')) return;
btn.classList.add('is-flash');
if (label) label.textContent = msg;
setTimeout(function () {
btn.classList.remove('is-flash');
if (label) label.textContent = orig;
}, 1400);
});
});