Voortgang: balk, stappen, laden, cirkel Nieuw
Vier voortgangsvormen in één set: stappen (2 van 4), lineaire balk met label en bedrag, een upload die naar succes of fout gaat, een cirkel en een onbepaalde laadbalk. Animeert naar de waarde zodra hij in beeld komt (IntersectionObserver + CSS-transities), met role="progressbar" en aria-valuenow. Bij reduced-motion staan de waarden direct goed en loopt de laadbalk niet.
Voortgang · vier vormen
Offerte aanvragenStap 2 van 4 · Woning
- Gegevens
- Woning
- Planning
- Bevestigen
Speeltuin Kralingen0%
€ 6.840 van € 9.000 opgehaald
plattegrond.pdf · 3,2 MB0%
Uploaden
0%
Profiel compleet
Agenda ophalen
Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- Voortgang: balk, stappen, laden, cirkel · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="prs">
<div class="prs__head"><span class="prs__eyebrow">Voortgang · vier vormen</span><span class="prs__btns"><button class="prs__btn prs__replay" type="button">Opnieuw</button><button class="prs__btn prs__fail" type="button">Toon fout</button></span></div>
<div class="prs__grid">
<div class="prs__col">
<div class="prs__item">
<div class="prs__lbl"><span>Offerte aanvragen</span><b>Stap 2 van 4 · Woning</b></div>
<ol class="prs__steps" aria-label="Offerte aanvragen, stap 2 van 4"><li class="is-done">Gegevens</li><li class="is-cur" aria-current="step">Woning</li><li>Planning</li><li>Bevestigen</li></ol>
</div>
<div class="prs__item">
<div class="prs__lbl"><span id="prs-l1">Speeltuin Kralingen</span><b class="prs__num" data-to="76">0%</b></div>
<div class="prs__bar" role="progressbar" aria-labelledby="prs-l1" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" data-to="76"><span></span></div>
<small class="prs__sub">€ 6.840 van € 9.000 opgehaald</small>
</div>
<div class="prs__item prs__up">
<div class="prs__lbl"><span id="prs-l2">plattegrond.pdf · 3,2 MB</span><b class="prs__unum">0%</b></div>
<div class="prs__bar prs__ubar" role="progressbar" aria-labelledby="prs-l2" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><span></span></div>
<small class="prs__msg" aria-live="polite">Uploaden</small>
</div>
</div>
<div class="prs__col prs__col--r">
<div class="prs__ring" role="progressbar" aria-label="Profiel compleet" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" data-to="80"><svg viewBox="0 0 44 44" aria-hidden="true"><circle class="prs__rbg" cx="22" cy="22" r="19"/><circle class="prs__rfg" cx="22" cy="22" r="19"/></svg><span class="prs__rnum">0%</span></div>
<small class="prs__cap">Profiel compleet</small>
<div class="prs__ind" role="progressbar" aria-label="Beschikbaarheid ophalen" aria-busy="true"><span></span></div>
<small class="prs__cap">Agenda ophalen</small>
</div>
</div>
</div>
</div>/* Voortgang: balk, stappen, laden, cirkel · NedDev · eigen werk. */
.prs{--ink:#0a0a0a;--pop:#F5480F;--pd:#b83a0b;--ok:#16833f;--err:#b3261e;position:absolute;inset:0;display:flex;flex-direction:column;gap:10px;padding:14px 16px;font-family:'Switzer',system-ui,sans-serif;color:var(--ink);overflow:auto}
.prs__head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.prs__eyebrow{font:600 10px/1 'JetBrains Mono',monospace;letter-spacing:.12em;text-transform:uppercase;color:#5b5b5b}
.prs__btns{display:flex;gap:6px}
.prs__btn{white-space:nowrap;font:600 10px/1 'JetBrains Mono',monospace;letter-spacing:.06em;text-transform:uppercase;padding:6px 8px;border:1px solid var(--ink);border-radius:100px;background:#fff;color:var(--ink);cursor:pointer;transition:background .15s,color .15s}
.prs__btn:hover{background:var(--ink);color:#fff}
.prs__btn:focus-visible{outline:2px solid var(--pd);outline-offset:2px}
.prs__grid{flex:1;display:grid;grid-template-columns:1fr 108px;gap:16px;align-items:center}
.prs__col{display:flex;flex-direction:column;gap:11px;min-width:0}
.prs__col--r{align-items:center;gap:5px}
.prs__lbl{display:flex;justify-content:space-between;gap:8px;font-size:12px;margin-bottom:5px}
.prs__lbl span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.prs__lbl b{font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.prs__steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:4px;font-size:0}
.prs__steps li{height:6px;border-radius:3px;background:#e1ded8;position:relative;overflow:hidden}
.prs__steps li::after{content:'';position:absolute;inset:0;background:var(--ink);transform:scaleX(0);transform-origin:left;transition:transform .45s cubic-bezier(.2,.7,.2,1)}
.prs.is-run .prs__steps li.is-done::after{transform:scaleX(1)}
.prs.is-run .prs__steps li.is-cur::after{background:var(--pop);transform:scaleX(.5);transition-delay:.4s}
.prs__bar{height:8px;border-radius:4px;background:#e1ded8;overflow:hidden}
.prs__bar span{display:block;height:100%;width:0;background:var(--ink);border-radius:4px;transition:width 1.1s cubic-bezier(.2,.7,.2,1)}
.prs__ubar span{background:var(--pop);transition:width .15s linear}
.prs__sub,.prs__msg,.prs__cap{display:block;margin-top:4px;font-size:11px;color:#5b5b5b}
.prs__cap{margin:0 0 6px;text-align:center}
.prs__up.is-ok .prs__ubar span{background:var(--ok)}
.prs__up.is-ok .prs__msg{color:var(--ok);font-weight:600}
.prs__up.is-err .prs__ubar span{background:var(--err)}
.prs__up.is-err .prs__msg,.prs__up.is-err .prs__unum{color:var(--err);font-weight:600}
.prs__ring{position:relative;width:84px;height:84px}
.prs__ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.prs__ring circle{fill:none;stroke-width:4.5}
.prs__rbg{stroke:#e1ded8}
.prs__rfg{stroke:var(--ink);stroke-linecap:round;stroke-dasharray:119.4;stroke-dashoffset:119.4;transition:stroke-dashoffset 1.2s cubic-bezier(.2,.7,.2,1)}
.prs__rnum{position:absolute;inset:0;display:grid;place-items:center;font:700 17px/1 'Switzer',system-ui,sans-serif;font-variant-numeric:tabular-nums}
.prs__ind{width:100%;height:6px;border-radius:3px;background:#e1ded8;overflow:hidden;position:relative}
.prs__ind span{position:absolute;top:0;bottom:0;width:40%;border-radius:3px;background:var(--ink);animation:prs-ind 1.3s cubic-bezier(.4,0,.2,1) infinite}
@keyframes prs-ind{from{left:-40%}to{left:100%}}
@media (max-width:420px){.prs{padding:12px;gap:8px}.prs__grid{grid-template-columns:1fr 84px;gap:12px}.prs__ring{width:68px;height:68px}.prs__rnum{font-size:14px}.prs__col{gap:8px}.prs__sub{display:none}}
@media (prefers-reduced-motion:reduce){.prs__bar span,.prs__rfg,.prs__steps li::after{transition:none}.prs__ind span{animation:none;left:0;width:100%;background:repeating-linear-gradient(90deg,#0a0a0a 0 8px,#5b5b5b 8px 16px)}}// Voortgang: balk, stappen, laden, cirkel · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.prs');if(!w)return;
var U='-'+Math.random().toString(36).slice(2,7);w.querySelectorAll('[id]').forEach(function(el){var o=el.id;el.id=o+U;w.querySelectorAll('[aria-labelledby="'+o+'"]').forEach(function(x){x.setAttribute('aria-labelledby',o+U);});});
var reduce=matchMedia('(prefers-reduced-motion: reduce)').matches,raf=0,up=w.querySelector('.prs__up'),ub=up.querySelector('.prs__ubar'),un=up.querySelector('.prs__unum'),msg=up.querySelector('.prs__msg');
function count(el,to,dur){var s=performance.now();if(reduce){el.textContent=to+'%';return;}(function f(n){var p=Math.min(1,(n-s)/dur),e=1-Math.pow(1-p,3);el.textContent=Math.round(to*e)+'%';if(p<1)requestAnimationFrame(f);})(s);}
function setBar(b,v){b.setAttribute('aria-valuenow',Math.round(v));b.firstElementChild.style.width=v+'%';}
function upload(failAt){cancelAnimationFrame(raf);up.classList.remove('is-ok','is-err');msg.textContent='Uploaden';var v=0,last=performance.now();
(function f(n){v+=Math.min(4,(n-last)/22)*(.6+Math.random()*.8);last=n;if(failAt&&v>=failAt){v=failAt;setBar(ub,v);un.textContent=Math.round(v)+'%';up.classList.add('is-err');msg.textContent='Upload mislukt bij '+failAt+'%. Controleer je verbinding en probeer opnieuw.';return;}
if(v>=100){setBar(ub,100);un.textContent='100%';up.classList.add('is-ok');msg.textContent='Geüpload, 3,2 MB';return;}setBar(ub,v);un.textContent=Math.round(v)+'%';raf=requestAnimationFrame(f);})(last);}
function run(failAt){w.classList.remove('is-run');void w.offsetWidth;w.querySelectorAll('.prs__bar[data-to]').forEach(function(b){setBar(b,0);});
var rf=w.querySelector('.prs__rfg');rf.style.strokeDashoffset=119.4;
requestAnimationFrame(function(){requestAnimationFrame(function(){w.classList.add('is-run');
w.querySelectorAll('.prs__bar[data-to]').forEach(function(b){setBar(b,+b.dataset.to);});w.querySelectorAll('.prs__num').forEach(function(n){count(n,+n.dataset.to,1100);});
var r=w.querySelector('.prs__ring'),to=+r.dataset.to;rf.style.strokeDashoffset=119.4*(1-to/100);r.setAttribute('aria-valuenow',to);count(w.querySelector('.prs__rnum'),to,1200);
upload(failAt);});});}
w.querySelector('.prs__replay').addEventListener('click',function(){run(0);});
w.querySelector('.prs__fail').addEventListener('click',function(){run(64);});
var done=false;if('IntersectionObserver' in window){var io=new IntersectionObserver(function(es){if(es[0].isIntersecting&&!done){done=true;run(0);io.disconnect();}},{threshold:.35});io.observe(w);}else run(0);
}
init(document.querySelector('.jouw-container'));
Meer uit cijfers & grafieken
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →