Stateful Loading Button
A submit button that morphs through idle, spinner and success states with a clean SVG tick.
<button class="lb" id="lb">
<span class="lb-label">Save changes</span>
<span class="lb-spin"></span>
<span class="lb-check"><svg viewBox="0 0 24 24" width="22" height="22"><path fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4 10-11"/></svg></span>
</button>*{box-sizing:border-box}
.lb{position:relative;min-width:180px;height:50px;border:0;border-radius:12px;background:#5b6cff;color:#fff;font-weight:700;font-size:15px;cursor:pointer;font-family:system-ui,sans-serif;overflow:hidden;transition:background .3s,min-width .3s}
.lb-spin,.lb-check{position:absolute;inset:0;display:grid;place-items:center;opacity:0}
.lb-spin::after{content:'';width:20px;height:20px;border:3px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;animation:sp .7s linear infinite}
@keyframes sp{to{transform:rotate(360deg)}}
.lb.loading .lb-label{opacity:0}.lb.loading .lb-spin{opacity:1}
.lb.done{background:#16a34a;min-width:50px}
.lb.done .lb-label{opacity:0}.lb.done .lb-check{opacity:1}var lb=document.getElementById('lb');
lb.onclick=function(){
if(lb.classList.contains('loading')||lb.classList.contains('done'))return;
lb.classList.add('loading');
setTimeout(function(){lb.classList.remove('loading');lb.classList.add('done');
setTimeout(function(){lb.classList.remove('done')},1600);
},1600);
};Free to use.This snippet is plain HTML, CSS and JavaScript with no dependencies. Copy any file, or download the whole thing as a single ready-to-open .html page.