Tarieventabel: sorteren, sticky, kaarten New
Price table for a physiotherapy practice: click a column header to sort (aria-sort, ascending/descending, aria-live announcement), sticky header, scroll shadows that only show when there is more to see (background-attachment: local, no JS) and cards with labels plus a sort select in narrow containers. Container query on the wrapper, so it also works in a sidebar.
Tarieven 2026Fysiotherapie Delft-Zuid
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.
<!-- Tarieventabel: sorteren, sticky, kaarten · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="tbr">
<div class="tbr__top"><div><span class="tbr__eyebrow">Tarieven 2026</span><strong class="tbr__title">Fysiotherapie Delft-Zuid</strong></div>
<label class="tbr__sel"><span>Sorteer</span><select><option value="">Standaard</option><option value="0">Behandeling</option><option value="2">Duur</option><option value="3">Tarief laag-hoog</option><option value="3d">Tarief hoog-laag</option><option value="4">Vergoeding</option></select></label></div>
<div class="tbr__scroll" tabindex="0" role="region" aria-label="Tarieven, scrollbaar">
<table class="tbr__t" role="table">
<caption class="tbr__sr">Tarieven fysiotherapie 2026, sorteerbaar per kolom</caption>
<thead role="rowgroup"><tr role="row">
<th role="columnheader" scope="col" aria-sort="none"><button type="button" data-k="0">Behandeling<span class="tbr__ic" aria-hidden="true"></span></button></th>
<th role="columnheader" scope="col" aria-sort="none"><button type="button" data-k="1">Code<span class="tbr__ic" aria-hidden="true"></span></button></th>
<th role="columnheader" scope="col" aria-sort="none" class="tbr__num"><button type="button" data-k="2">Duur<span class="tbr__ic" aria-hidden="true"></span></button></th>
<th role="columnheader" scope="col" aria-sort="none" class="tbr__num"><button type="button" data-k="3">Tarief<span class="tbr__ic" aria-hidden="true"></span></button></th>
<th role="columnheader" scope="col" aria-sort="none"><button type="button" data-k="4">Vergoeding<span class="tbr__ic" aria-hidden="true"></span></button></th>
</tr></thead>
<tbody role="rowgroup"></tbody>
</table>
</div>
<p class="tbr__sr" aria-live="polite"></p>
</div>
</div>/* Tarieventabel: sorteren, sticky, kaarten · NedDev · eigen werk. */
.tbr{--ink:#0a0a0a;--pop:#F5480F;--pd:#b83a0b;--line:rgba(10,10,10,.12);position:absolute;inset:0;display:flex;flex-direction:column;gap:8px;padding:12px 14px;font-family:'Switzer',system-ui,sans-serif;color:var(--ink);background:#f4f3f1;container:tbr/inline-size}
.tbr__top{display:flex;align-items:flex-end;justify-content:space-between;gap:10px}
.tbr__eyebrow{display:block;font:600 9.5px/1.2 'JetBrains Mono',monospace;letter-spacing:.12em;text-transform:uppercase;color:#5b5b5b}
.tbr__title{font-size:14px}
.tbr__sel{display:none;align-items:center;gap:6px;font:600 9.5px/1 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:#5b5b5b}
.tbr__sel select{font:500 12px/1 'Switzer',system-ui,sans-serif;padding:6px 8px;border:1px solid var(--ink);border-radius:4px;background:#fff;color:var(--ink)}
.tbr__sel select:focus-visible{outline:2px solid var(--pd);outline-offset:2px}
.tbr__scroll{flex:1;min-height:0;overflow:auto;border:1px solid var(--line);border-radius:4px;background:linear-gradient(90deg,#fff 30%,rgba(255,255,255,0)) left center/36px 100% no-repeat local,linear-gradient(270deg,#fff 30%,rgba(255,255,255,0)) right center/36px 100% no-repeat local,linear-gradient(0deg,#fff 30%,rgba(255,255,255,0)) center bottom/100% 30px no-repeat local,radial-gradient(farthest-side at 0 50%,rgba(10,10,10,.3),rgba(10,10,10,0)) left center/16px 100% no-repeat scroll,radial-gradient(farthest-side at 100% 50%,rgba(10,10,10,.3),rgba(10,10,10,0)) right center/16px 100% no-repeat scroll,radial-gradient(farthest-side at 50% 100%,rgba(10,10,10,.18),rgba(10,10,10,0)) center bottom/100% 10px no-repeat scroll;background-color:#fff}
.tbr__scroll:focus-visible{outline:2px solid var(--pd);outline-offset:2px}
.tbr__t{width:100%;min-width:470px;border-collapse:separate;border-spacing:0;font-size:12.5px}
.tbr__t th{position:sticky;top:0;z-index:1;background:#fff;text-align:left;padding:0;border-bottom:1px solid var(--ink);white-space:nowrap}
.tbr__t th button{display:flex;align-items:center;gap:5px;width:100%;padding:8px 10px;border:0;background:none;font:600 10px/1 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:var(--ink);cursor:pointer;text-align:left;transition:background .15s}
.tbr__t th.tbr__num button{justify-content:flex-end}
.tbr__t th button:hover{background:#f4f3f1}
.tbr__t th button:focus-visible{outline:2px solid var(--pd);outline-offset:-2px}
.tbr__ic{width:8px;height:10px;opacity:.35;background:linear-gradient(var(--ink),var(--ink)) center 0/0 0 no-repeat;position:relative}
.tbr__ic::before,.tbr__ic::after{content:'';position:absolute;left:0;border:4px solid transparent}
.tbr__ic::before{top:-4px;border-bottom-color:var(--ink)}
.tbr__ic::after{bottom:-4px;border-top-color:var(--ink)}
.tbr__t th[aria-sort=ascending] .tbr__ic,.tbr__t th[aria-sort=descending] .tbr__ic{opacity:1}
.tbr__t th[aria-sort=ascending] .tbr__ic::after{border-top-color:transparent}
.tbr__t th[aria-sort=descending] .tbr__ic::before{border-bottom-color:transparent}
.tbr__t th[aria-sort=ascending],.tbr__t th[aria-sort=descending]{box-shadow:inset 0 -3px 0 var(--pop)}
.tbr__t td{padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap}
.tbr__t tr:last-child td{border-bottom:0}
.tbr__t tbody tr:hover td{background:rgba(245,72,15,.05)}
.tbr__t td:first-child{font-weight:600}
.tbr__code{font:500 11.5px/1 'JetBrains Mono',monospace;color:#5b5b5b}
.tbr__t td.tbr__num{text-align:right;font-variant-numeric:tabular-nums}
.tbr__b{display:inline-block;padding:3px 7px;border-radius:100px;font-size:11px;font-weight:600;border:1px solid}
.tbr__b--a{background:#e8f5ee;color:#0f5f30;border-color:#bfe2cd}
.tbr__b--c{background:#eaf1fb;color:#1e4fa8;border-color:#c4d6f2}
.tbr__b--n{background:#f4f3f1;color:#4a4a4a;border-color:#dcd9d3}
.tbr__t tbody.is-sorted tr{animation:tbr-fl .4s ease}
@keyframes tbr-fl{from{opacity:.4}}
.tbr__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;margin:0}
@container tbr (max-width:380px){
.tbr__sel{display:flex}
.tbr__scroll{background:none;border:0;overflow:auto}
.tbr__t{min-width:0}
.tbr__t,.tbr__t tbody,.tbr__t tr,.tbr__t td{display:block}
.tbr__t thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
.tbr__t tr{background:#fff;border:1px solid var(--line);border-radius:4px;padding:8px 10px;margin-bottom:6px;display:grid;grid-template-columns:1fr auto;gap:2px 10px}
.tbr__t td{padding:0;border:0;white-space:normal;display:flex;gap:6px;align-items:baseline;font-size:12px}
.tbr__t td::before{content:attr(data-label);font:600 9px/1.6 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:#5b5b5b}
.tbr__t td:first-child{grid-column:1/-1;font-size:13px}
.tbr__t td:first-child::before{display:none}
.tbr__t td.tbr__num{text-align:left}
.tbr__t tbody tr:hover td{background:none}
}
@media (prefers-reduced-motion:reduce){.tbr__t tbody.is-sorted tr{animation:none}}// Tarieventabel: sorteren, sticky, kaarten · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.tbr');if(!w)return;
var R=[['Intake en onderzoek','1864',30,5250,'Aanvullend'],['Reguliere zitting','1000',25,4200,'Aanvullend'],['Manuele therapie','1200',30,5800,'Aanvullend'],['Oedeemtherapie','1100',30,4500,'Basis bij chronisch'],['Telefonisch consult','1870',15,2100,'Aanvullend'],['Dry needling','1009',25,4750,'Niet vergoed'],['Sportmassage','1910',45,5500,'Niet vergoed'],['Shockwave-therapie','1950',20,4900,'Niet vergoed']];
var L=['Behandeling','Code','Duur','Tarief','Vergoeding'],tb=w.querySelector('tbody'),sr=w.querySelectorAll('.tbr__sr')[1],sel=w.querySelector('.tbr__sel select'),ths=[].slice.call(w.querySelectorAll('thead th'));
function eur(c){return '€ '+(c/100).toFixed(2).replace('.',',');}
function badge(v){var c=v==='Aanvullend'?'a':v==='Niet vergoed'?'n':'c';return '<span class="tbr__b tbr__b--'+c+'">'+v+'</span>';}
function draw(rows){tb.innerHTML=rows.map(function(r){return '<tr role="row"><td role="cell" data-label="'+L[0]+'">'+r[0]+'</td><td role="cell" data-label="'+L[1]+'"><span class="tbr__code">'+r[1]+'</span></td><td role="cell" class="tbr__num" data-label="'+L[2]+'">'+r[2]+' min</td><td role="cell" class="tbr__num" data-label="'+L[3]+'">'+eur(r[3])+'</td><td role="cell" data-label="'+L[4]+'">'+badge(r[4])+'</td></tr>';}).join('');}
var key=-1,dir=1;
function sort(k,d,announce){key=k;dir=d;var rows=R.slice();if(k>=0)rows.sort(function(a,b){var x=a[k],y=b[k];return (typeof x==='number'?x-y:x.localeCompare(y,'nl'))*d;});
draw(rows);tb.classList.remove('is-sorted');void tb.offsetWidth;tb.classList.add('is-sorted');
ths.forEach(function(th,i){th.setAttribute('aria-sort',i===k?(d>0?'ascending':'descending'):'none');});
sel.value=k<0?'':(k===3&&d<0?'3d':String(k));
if(announce)sr.textContent=k<0?'Standaardvolgorde':'Gesorteerd op '+L[k]+', '+(d>0?'oplopend':'aflopend');}
ths.forEach(function(th){var b=th.querySelector('button'),k=+b.dataset.k;b.addEventListener('click',function(){sort(k,key===k?-dir:1,true);});});
sel.addEventListener('change',function(){var v=sel.value;if(!v)sort(-1,1,true);else if(v==='3d')sort(3,-1,true);else sort(+v,1,true);});
sort(-1,1,false);
}
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 →