Laadstanden in de knop New
Three ways to show a button is busy, inside the button itself: a spinning ring, three bouncing dots and a bar that fills up. The label stays invisible in place so the button keeps its width; aria-busy and a status line tell screen readers what is going on, and double clicks do nothing. Use the ring for payments, the dots for sending and the bar when you really know the progress (uploads). With reduced motion they pulse calmly instead of spinning.
How to use it
- Paste the HTML where the buttons should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Laadstanden in de knop · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="kls">
<div class="kls__col">
<button class="kls__b" type="button" data-k="spin" data-msg="Betaling wordt verwerkt" data-ok="Betaald"><span class="kls__l">Betaal € 84,50</span><span class="kls__ld" aria-hidden="true"><span class="kls__spin"></span></span></button>
<button class="kls__b" type="button" data-k="dots" data-msg="Aanvraag wordt verstuurd" data-ok="Verstuurd"><span class="kls__l">Offerte aanvragen</span><span class="kls__ld" aria-hidden="true"><span class="kls__dots"><span></span><span></span><span></span></span></span></button>
<button class="kls__b" type="button" data-k="bar" data-msg="Foto's worden geüpload" data-ok="Geüpload"><span class="kls__l">Upload 6 foto's</span><span class="kls__ld" aria-hidden="true"><span class="kls__bar"><span class="kls__fill"></span></span></span></button>
</div>
<p class="kls__st" role="status"></p>
</div>
</div>/* Laadstanden in de knop · NedDev · eigen werk. */
.kls{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:10px;background:#f4f3f1;font-family:'Switzer',sans-serif;color:#0a0a0a}
.kls *,.kls *::before,.kls *::after{box-sizing:border-box}
.kls__col{display:flex;flex-direction:column;gap:9px;width:min(230px,100%)}
.kls__b{appearance:none;position:relative;display:grid;place-items:center;min-height:44px;margin:0;padding:0 18px;border:0;border-radius:4px;background:#0a0a0a;color:#fff;font:600 15px/1 'Switzer',sans-serif;cursor:pointer;transition:background-color .16s ease,color .16s ease}
.kls__b:nth-child(2){background:#F5480F;color:#0a0a0a}
.kls__b:nth-child(3){background:#fff;color:#0a0a0a;box-shadow:inset 0 0 0 1px #0a0a0a}
.kls__b:hover{background:#2b2926}
.kls__b:nth-child(2):hover{background:#ff5d24}
.kls__b:nth-child(3):hover{background:#faf9f7}
.kls__b:focus-visible{outline:2px solid #F5480F;outline-offset:3px}
.kls__b:nth-child(2):focus-visible{outline-color:#0a0a0a}
.kls__l,.kls__ld{grid-area:1/1}
.kls__ld{display:none;width:100%;align-items:center;justify-content:center}
.kls__b.is-busy{cursor:progress}
.kls__b.is-busy .kls__l{visibility:hidden}
.kls__b.is-busy .kls__ld{display:flex}
.kls__spin{width:20px;height:20px;border-radius:50%;border:2.5px solid rgba(255,255,255,.28);border-top-color:#fff;animation:kls-rot .7s linear infinite}
@keyframes kls-rot{to{transform:rotate(360deg)}}
.kls__dots{display:flex;gap:6px}
.kls__dots span{width:7px;height:7px;border-radius:50%;background:#0a0a0a;animation:kls-hop 1s cubic-bezier(.4,0,.2,1) infinite}
.kls__dots span:nth-child(2){animation-delay:.15s}
.kls__dots span:nth-child(3){animation-delay:.3s}
@keyframes kls-hop{0%,60%,100%{transform:translateY(0);opacity:.55}30%{transform:translateY(-6px);opacity:1}}
.kls__bar{display:block;width:62%;height:5px;background:#e8e5e0;border-radius:4px;overflow:hidden}
.kls__fill{display:block;width:0;height:100%;background:#F5480F}
.kls__b.is-ok .kls__l{color:inherit}
.kls__st{min-height:18px;margin:0;font:400 13px/1.35 'Switzer',sans-serif;color:#5d5953;text-align:center}
@media (prefers-reduced-motion:reduce){.kls__spin{animation:kls-puls 1.4s ease-in-out infinite;border-top-color:rgba(255,255,255,.28);background:rgba(255,255,255,.18)}.kls__dots span{animation:kls-puls 1.4s ease-in-out infinite}@keyframes kls-puls{0%,100%{opacity:.4}50%{opacity:1}}}// Laadstanden in de knop · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.kls');if(!w)return;
var st=w.querySelector('.kls__st');
[].forEach.call(w.querySelectorAll('.kls__b'),function(b){var l=b.querySelector('.kls__l'),orig=l.textContent;
b.addEventListener('click',function(){if(b.classList.contains('is-busy'))return;
b.classList.add('is-busy');b.setAttribute('aria-busy','true');b.setAttribute('aria-disabled','true');st.textContent=b.getAttribute('data-msg')+'...';
var dur=2400,fill=b.querySelector('.kls__fill');
function finish(){b.classList.remove('is-busy');b.removeAttribute('aria-busy');b.removeAttribute('aria-disabled');l.textContent=b.getAttribute('data-ok');st.textContent=b.getAttribute('data-ok')+'.';
setTimeout(function(){l.textContent=orig;if(fill)fill.style.width='0';},1800);}
if(fill){var t0=performance.now();(function step(t){var p=Math.min(1,(t-t0)/dur);fill.style.width=(p*100)+'%';if(p<1)requestAnimationFrame(step);else setTimeout(finish,150);})(t0);}
else setTimeout(finish,dur);});});
}
init(document.querySelector('.jouw-container'));
More buttons
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →