Sortable Data Table
Data table with selectable rows, sortable columns, status badges and a sticky header. Click a header to sort.
Preview and code
index.html
<div class="tb"><small class="cn"></small><div class="sh"><table><thead></thead><tbody></tbody></table></div></div>
styles.css
.tb{width:100%;height:100%;min-height:380px;box-sizing:border-box;padding:20px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-family:system-ui,-apple-system,sans-serif;color:#eee;}.cn{width:100%;max-width:520px;font-size:11px;color:#777;margin-bottom:6px}.sh{width:100%;max-width:520px;max-height:290px;overflow:auto;border:1px solid #2a2a2a;border-radius:14px;background:#161616}table{border-collapse:collapse;width:100%;min-width:480px;font-size:12px}th{position:sticky;top:0;background:#1d1d1d;text-align:left;font-weight:600;color:#999;padding:10px 12px;cursor:pointer;user-select:none;white-space:nowrap;z-index:1}th:hover{color:#fff}td{padding:11px 12px;border-top:1px solid #222;white-space:nowrap}tr.sl td{background:rgba(10,132,255,.1)}.ck{width:15px;height:15px;border-radius:4px;border:1.5px solid #444;display:inline-grid;place-items:center;font-size:10px;color:#fff;cursor:pointer;vertical-align:middle}.ck.on{background:#0a84ff;border-color:#0a84ff}.bd{padding:3px 8px;border-radius:99px;font-size:10px;font-weight:600}.Active{background:rgba(34,197,94,.15);color:#34d399}.Invited{background:rgba(245,158,11,.15);color:#fbbf24}.Suspended{background:rgba(239,68,68,.15);color:#f87171}.ar{margin-left:4px;font-size:9px}
script.js
(function(){var R=document.querySelector(".tb"),th=R.querySelector("thead"),tb=R.querySelector("tbody"),cn=R.querySelector(".cn"),F=["Leo","Jude","Ren","Eli","Mia","Noa","Sam","Zoe","Ari","Kai"],L=["Marsh","Reyes","Quinn","Sato","Ortiz","Khan","Berg","Lopez","Ford","Cole"],RO=["Admin","Editor","Viewer","Owner"],ST=["Suspended","Active","Invited"],D=[];for(var i=0;i<30;i++){var f=F[i%10],l=L[(i*3)%10];D.push({id:i,name:f+" "+l,email:(f+"."+l+(100+i*7)).toLowerCase()+"@example.dev",role:RO[i%4],status:ST[(i*2)%3],mrr:[49,99,199,499][(i*5)%4]})}var key=null,dir=1,sel={};var C=[["name","Name"],["email","Email"],["role","Role"],["status","Status"],["mrr","MRR"]];function draw(){var rows=D.slice();if(key)rows.sort(function(a,b){var x=a[key],y=b[key];return(x>y?1:x<y?-1:0)*dir});var n=Object.keys(sel).length;cn.textContent=n?n+" selected":D.length+" rows";th.innerHTML="";var tr=document.createElement("tr"),h=document.createElement("th");h.innerHTML="<span class=\"ck"+(n===D.length?" on":"")+"\">"+(n===D.length?"✓":n?"–":"")+"</span>";h.onclick=function(){if(n===D.length)sel={};else D.forEach(function(d){sel[d.id]=1});draw()};tr.appendChild(h);C.forEach(function(c){var t=document.createElement("th");t.innerHTML=c[1]+"<span class=\"ar\">"+(key===c[0]?(dir>0?"▲":"▼"):"")+"</span>";t.onclick=function(){if(key===c[0])dir=-dir;else{key=c[0];dir=1}draw()};tr.appendChild(t)});th.appendChild(tr);tb.innerHTML="";rows.forEach(function(d){var r=document.createElement("tr");if(sel[d.id])r.className="sl";r.innerHTML="<td><span class=\"ck"+(sel[d.id]?" on":"")+"\">"+(sel[d.id]?"✓":"")+"</span></td><td><b>"+d.name+"</b></td><td>"+d.email+"</td><td>"+d.role+"</td><td><span class=\"bd "+d.status+"\">"+d.status+"</span></td><td>$"+d.mrr+"</td>";r.firstChild.onclick=function(){if(sel[d.id])delete sel[d.id];else sel[d.id]=1;draw()};tb.appendChild(r)})}draw()})();