KPI-tegels met delta New
Three KPI tiles with a big number, delta (arrow + percent or points), comparison with the previous period and a sparkline with the dashed previous period. Colour follows good or bad, not up or down: lower cost per lead is green. The week/month switch shows the loading state (aria-busy + skeleton). For client dashboards and results pages.
Installatiebedrijf Wolters · Apeldoorn
Resultaten website
Bijgewerkt ma 6 okt, 21:07 ·
How to use it
- Paste the HTML where the numbers & charts 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.
<!-- KPI-tegels met delta · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="kpt"><div class="kpt__hd"><div><p class="kpt__k">Installatiebedrijf Wolters · Apeldoorn</p><p class="kpt__t">Resultaten website</p></div><div class="kpt__sw" role="group" aria-label="Periode"><button type="button" class="kpt__b" aria-pressed="true" data-p="w">Week</button><button type="button" class="kpt__b" aria-pressed="false" data-p="m">Maand</button></div></div>
<div class="kpt__grid" aria-live="polite"></div><p class="kpt__f">Bijgewerkt ma 6 okt, 21:07 · <span class="kpt__per"></span></p></div>
</div>/* KPI-tegels met delta · NedDev · eigen werk. */
.kpt{position:absolute;inset:0;box-sizing:border-box;padding:14px 16px 10px;background:#f4f3f1;color:#0a0a0a;font-family:'Switzer',system-ui,sans-serif;display:flex;flex-direction:column;gap:9px}
.kpt__hd{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.kpt__k{margin:0;font:500 10px/1.3 'JetBrains Mono',monospace;color:#5f5e5a}
.kpt__t{margin:2px 0 0;font-size:15px;font-weight:650;line-height:1.2}
.kpt__sw{display:flex;border:1px solid rgba(10,10,10,.18);border-radius:999px;padding:2px;flex:none;background:#fff}
.kpt__b{font:600 11px 'Switzer',system-ui,sans-serif;border:0;background:none;color:#5f5e5a;padding:4px 10px;border-radius:999px;cursor:pointer;transition:background .15s,color .15s}
.kpt__b:hover{color:#0a0a0a}.kpt__b[aria-pressed="true"]{background:#0a0a0a;color:#fff}
.kpt__b:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.kpt__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;flex:1;min-height:0}
.kpt__c{background:#fff;border:1px solid rgba(10,10,10,.1);border-radius:6px;padding:9px 10px 8px;display:flex;flex-direction:column;gap:3px;min-width:0}
.kpt__l{margin:0;font-size:11px;color:#5f5e5a;line-height:1.25;min-height:2.5em}
.kpt__v{margin:0;font-size:22px;font-weight:700;letter-spacing:-.01em;font-variant-numeric:tabular-nums;line-height:1.1;white-space:nowrap}
.kpt__d{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:650;font-variant-numeric:tabular-nums;width:max-content;padding:1px 5px 1px 3px;border-radius:3px}
.kpt__d svg{width:10px;height:10px}
.kpt__d.is-good{color:#13663b;background:#e3f2e8}.kpt__d.is-bad{color:#a8200d;background:#fbe7e2}.kpt__d.is-flat{color:#5f5e5a;background:#ecebe8}
.kpt__vs{margin:0;font-size:10.5px;color:#6b6a66;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kpt__sp{width:100%;height:26px;margin-top:auto;display:block}
.kpt__sp .c{fill:none;stroke:#0a0a0a;stroke-width:1.75;vector-effect:non-scaling-stroke;stroke-linejoin:round}
.kpt__sp .p{fill:none;stroke:#a3a19c;stroke-width:1.25;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}
.kpt__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.kpt__f{margin:0;font:500 9.5px 'JetBrains Mono',monospace;color:#6b6a66}
.kpt__grid[aria-busy="true"] .kpt__v,.kpt__grid[aria-busy="true"] .kpt__d,.kpt__grid[aria-busy="true"] .kpt__vs,.kpt__grid[aria-busy="true"] .kpt__sp{color:transparent;background:linear-gradient(90deg,#ecebe8 30%,#f7f6f4 50%,#ecebe8 70%);background-size:300% 100%;border-radius:3px;animation:kpt-sh 1s linear infinite}
.kpt__grid[aria-busy="true"] .kpt__sp *,.kpt__grid[aria-busy="true"] .kpt__d svg{visibility:hidden}
@keyframes kpt-sh{to{background-position:-150% 0}}
.kpt__c.is-new{animation:kpt-in .35s ease-out}
@keyframes kpt-in{from{opacity:.3}}
@media(max-width:420px){.kpt{padding:10px 10px 8px;gap:7px}.kpt__grid{gap:6px}.kpt__c{padding:7px 7px 6px}.kpt__v{font-size:18px}.kpt__l{font-size:10px}.kpt__vs{font-size:9.5px}.kpt__sp{height:20px}.kpt__f{display:none}}
@media(prefers-reduced-motion:reduce){.kpt__grid[aria-busy="true"] *{animation:none!important}.kpt__c.is-new{animation:none}}// KPI-tegels met delta · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.kpt');if(!w)return;var grid=w.querySelector('.kpt__grid'),per=w.querySelector('.kpt__per');
function wave(n,b,a,s){var r=[];for(var i=0;i<n;i++)r.push(b+a*Math.sin(i*s*.35)+a*.25*Math.sin(i*s*1.3+1));return r;}
var D={w:{lab:'vergeleken met vorige week',pl:'was',k:[
{l:'Offerte-aanvragen',v:48,p:42,f:'n',up:true,s:[5,7,6,8,6,9,7],sp:[6,5,7,6,5,7,6]},
{l:'Kosten per aanvraag',v:18.40,p:20.25,f:'e',up:false,s:[21.2,20.6,19.9,19.4,19,18.7,18.4],sp:[20.1,20.4,20,20.6,20.2,20.3,20.25]},
{l:'Conversie website',v:3.2,p:3.6,f:'p',up:true,s:[3.7,3.5,3.6,3.3,3.4,3.1,3.2],sp:[3.5,3.6,3.5,3.7,3.6,3.6,3.6]}]},
m:{lab:'vergeleken met vorige maand',pl:'was',k:[
{l:'Offerte-aanvragen',v:196,p:184,f:'n',up:true,s:wave(30,6.5,1.6,.9),sp:wave(30,6.1,1.4,.7)},
{l:'Kosten per aanvraag',v:19.10,p:18.71,f:'e',up:false,s:wave(30,19,.8,.5),sp:wave(30,18.7,.7,.6)},
{l:'Conversie website',v:3.5,p:3.3,f:'p',up:true,s:wave(30,3.5,.25,.8),sp:wave(30,3.3,.2,.6)}]}};
function fmt(v,f){if(f==='e')return '€ '+v.toLocaleString('nl-NL',{minimumFractionDigits:2,maximumFractionDigits:2});if(f==='p')return v.toLocaleString('nl-NL',{minimumFractionDigits:1,maximumFractionDigits:1})+'%';return v.toLocaleString('nl-NL');}
function spark(c,p){var all=c.concat(p),mn=Math.min.apply(0,all),mx=Math.max.apply(0,all),rg=(mx-mn)||1;function d(a){return a.map(function(v,i){return (i?'L':'M')+(i/(a.length-1)*100).toFixed(2)+','+(25-(v-mn)/rg*22).toFixed(2);}).join('');}
return '<svg class="kpt__sp" viewBox="0 0 100 28" preserveAspectRatio="none" aria-hidden="true"><path class="p" d="'+d(p)+'"/><path class="c" d="'+d(c)+'"/></svg>';}
var UP='<svg viewBox="0 0 10 10" aria-hidden="true"><path d="M5 1.5 9 7H1z" fill="currentColor"/></svg>',DN='<svg viewBox="0 0 10 10" aria-hidden="true"><path d="M5 8.5 1 3h8z" fill="currentColor"/></svg>',FL='<svg viewBox="0 0 10 10" aria-hidden="true"><path d="M1.5 4h7v2h-7z" fill="currentColor"/></svg>';
function render(key){var P=D[key];per.textContent=P.lab;grid.innerHTML=P.k.map(function(k){var diff=k.v-k.p,dir=Math.abs(diff)<1e-9?0:(diff>0?1:-1);
var amt=k.f==='p'?Math.abs(diff).toLocaleString('nl-NL',{maximumFractionDigits:1})+' pt':Math.abs(diff/k.p*100).toLocaleString('nl-NL',{minimumFractionDigits:1,maximumFractionDigits:1})+'%';
var good=dir===0?'flat':((dir>0)===k.up?'good':'bad');var word=dir===0?'gelijk':(dir>0?'gestegen':'gedaald');var verdict=good==='good'?', goed':good==='bad'?', let op':'';
return '<div class="kpt__c is-new"><p class="kpt__l">'+k.l+'</p><p class="kpt__v">'+fmt(k.v,k.f)+'</p><span class="kpt__d is-'+good+'">'+(dir>0?UP:dir<0?DN:FL)+'<span class="kpt__sr">'+word+' met </span>'+amt+'<span class="kpt__sr">'+verdict+'</span></span><p class="kpt__vs">'+P.pl+' '+fmt(k.p,k.f)+'</p>'+spark(k.s,k.sp)+'</div>';}).join('');}
render('w');var t;
w.querySelectorAll('.kpt__b').forEach(function(b){b.addEventListener('click',function(){if(b.getAttribute('aria-pressed')==='true')return;w.querySelectorAll('.kpt__b').forEach(function(x){x.setAttribute('aria-pressed',x===b?'true':'false');});grid.setAttribute('aria-busy','true');clearTimeout(t);t=setTimeout(function(){render(b.dataset.p);grid.setAttribute('aria-busy','false');},550);});});
}
init(document.querySelector('.jouw-container'));
More numbers & charts
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 →