.cfb-load {
position: relative;
width: 52px; height: 52px;
border: 0; border-radius: 50%;
background: linear-gradient(135deg, #2eb88a, #06b6d4);
color: #fff;
cursor: pointer;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 6px 16px rgba(46,184,138,0.35);
transition: transform 0.2s, opacity 0.2s;
}
.cfb-load:hover { transform: translateY(-2px); }
.cfb-load:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.cfb-load:disabled { cursor: wait; opacity: 0.85; }
.cfb-load svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: opacity 0.2s; }
.cfb-load-spin {
position: absolute; inset: 14px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: #fff;
border-radius: 50%;
opacity: 0;
animation: cfb-load-rot 0.7s linear infinite;
}
.cfb-load.is-loading .cfb-load-icon { opacity: 0; }
.cfb-load.is-loading .cfb-load-spin { opacity: 1; }
@keyframes cfb-load-rot { to { transform: rotate(360deg); } } <button type="button" class="cfb-load" aria-label="Save changes" data-cfb-load>
<svg class="cfb-load-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
<polyline points="17 21 17 13 7 13 7 21" />
<polyline points="7 3 7 8 15 8" />
</svg>
<span class="cfb-load-spin" aria-hidden="true"></span>
</button> // Loading FAB — click triggers spinner state, returns to ready after 1.5s
document.querySelectorAll('[data-cfb-load]').forEach(function (btn) {
btn.addEventListener('click', function () {
if (btn.classList.contains('is-loading')) return;
btn.classList.add('is-loading');
btn.disabled = true;
setTimeout(function () {
btn.classList.remove('is-loading');
btn.disabled = false;
}, 1500);
});
}); Live preview Edit any tab — preview updates live Ready