{
    "id": "bhaskar/status-bars",
    "kind": "component",
    "title": "Status Bars",
    "summary": "Ninety-day uptime strip with a gliding tooltip, incident details and keyboard navigation.",
    "author": "Admin",
    "stars": 0,
    "url": "https://ownlyplans.com/bhaskar/status-bars/",
    "stack": [
        "CSS",
        "HTML",
        "JavaScript"
    ],
    "tools": [
        "claude-code",
        "cursor",
        "lovable"
    ],
    "difficulty": "beginner",
    "license": "mit",
    "version": "1.0",
    "overview": "Ninety-day uptime strip with a gliding tooltip, incident details and keyboard navigation. Ported to plain HTML, CSS and JavaScript from ObsidianUI (github.com/Atharvsinh-codez/ObsidianUI), which is released under the MIT License. The original copyright notice is kept in the code.",
    "steps": [],
    "files": [],
    "install": "",
    "how_to_use": "Use the component code (html, css, js, or react) directly, or run the install command.",
    "component": {
        "category": "Charts",
        "html": "<!--\n  Ported to plain HTML/CSS/JS for OwnlyPlans from ObsidianUI (https://github.com/Atharvsinh-codez/ObsidianUI)\n  MIT License — Copyright (c) 2026 ObsidianUI\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n  documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation\n  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so, subject to the following conditions:\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.\n-->\n<div class=\"op-sb-stage\">\n  <div class=\"op-sb-list\">\n    <div class=\"op-status-bars\" data-label=\"Component registry\" data-incidents='[{\"ago\":70,\"degraded\":14,\"title\":\"Slow registry responses\"},{\"ago\":57,\"downtime\":23,\"title\":\"Registry deploy rolled back\"},{\"ago\":43,\"degraded\":9,\"title\":\"Elevated error rate\"},{\"ago\":24,\"degraded\":6,\"title\":\"Elevated error rate\"}]'></div>\n    <div class=\"op-status-bars\" data-label=\"Docs and previews\" data-incidents='[{\"ago\":64,\"degraded\":32,\"title\":\"Search index rebuilding\"},{\"ago\":31,\"downtime\":11,\"title\":\"CDN cache purge failed\"},{\"ago\":16,\"degraded\":18,\"title\":\"Preview images loading slowly\"}]'></div>\n  </div>\n</div>",
        "css": "/* Ported from ObsidianUI (MIT License, Copyright (c) 2026 ObsidianUI) */\n.op-sb-stage{min-height:320px;display:grid;place-items:center;padding:3rem 1.5rem;background:#fff;color:#0a0a0a;font-family:Inter,system-ui,sans-serif;--muted:#737373;--border:#e5e5e5;--pop:#fff}\n.op-sb-list{width:100%;max-width:640px;display:flex;flex-direction:column;gap:2rem}\n.op-status-bars{--up:oklch(.7 .16 152);--degraded:oklch(.8 .15 78);--down:oklch(.63 .21 25);--empty:color-mix(in oklab,currentColor 9%,transparent);display:flex;flex-direction:column;width:100%;min-width:0}\n.op-sb-head{margin-bottom:10px;display:flex;align-items:center;justify-content:space-between;gap:12px}\n.op-sb-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;letter-spacing:-.01em}\n.op-sb-now{display:flex;flex-shrink:0;align-items:center;gap:6px;font-size:12px;color:var(--muted)}\n.op-sb-dotwrap{position:relative;display:grid;width:8px;height:8px;place-items:center}\n.op-sb-dot{width:6px;height:6px;border-radius:999px}\n.op-sb-ping{position:absolute;inset:0;border-radius:999px;animation:op-sb-ping 1.8s cubic-bezier(0,0,.2,1) infinite}\n.op-sb-wrap{position:relative}\n.op-sb-strip{display:flex;height:32px;gap:2px;border-radius:3px;outline:none}\n.op-sb-strip:focus-visible{outline:1px solid #a3a3a3;outline-offset:3px}\n.op-sb-bar{min-width:0;flex:1;transform-origin:bottom;border-radius:2px;transition:opacity 150ms ease-out,transform 150ms ease-out}\n.op-sb-strip[data-hovering] .op-sb-bar{opacity:.35}\n.op-sb-strip[data-hovering] .op-sb-bar[data-active]{opacity:1;transform:scaleY(1.08)}\n.s-up{background:color-mix(in oklab,var(--up) 75%,transparent)}.s-degraded{background:var(--degraded)}.s-down{background:var(--down)}.s-none{background:var(--empty)}\n.d-up{background:var(--up)}.d-degraded{background:var(--degraded)}.d-down{background:var(--down)}.d-none{background:color-mix(in oklab,var(--muted) 50%,transparent)}\n.op-sb-tip{pointer-events:none;position:absolute;left:0;top:0;z-index:20;opacity:0;transition:opacity 140ms cubic-bezier(.22,1,.36,1),translate 140ms cubic-bezier(.22,1,.36,1);translate:0 3px}\n.op-sb-tip[data-show]{opacity:1;translate:0 0}\n.op-sb-tip-inner{transform:translateY(-100%);padding-bottom:10px}\n.op-sb-card{width:max-content;max-width:16rem;border-radius:8px;border:1px solid var(--border);background:var(--pop);padding:8px 10px;font-size:12px;line-height:16px;box-shadow:0 10px 15px -3px rgb(0 0 0/.1),0 4px 6px -4px rgb(0 0 0/.1)}\n.op-sb-row{display:flex;align-items:baseline;justify-content:space-between;gap:16px}\n.op-sb-pct{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-variant-numeric:tabular-nums;color:var(--muted)}\n.op-sb-st{margin-top:4px;display:flex;align-items:center;gap:6px;color:var(--muted)}\n.op-sb-sub{color:color-mix(in oklab,var(--muted) 80%,transparent)}\n.op-sb-inc{margin-top:6px;border-top:1px solid var(--border);padding-top:6px;color:var(--muted)}\n.op-sb-axis{margin-top:8px;display:flex;align-items:center;gap:12px;font-size:11px;color:color-mix(in oklab,var(--muted) 80%,transparent)}\n.op-sb-axis span{flex-shrink:0;font-variant-numeric:tabular-nums}.op-sb-axis i{height:1px;flex:1;background:var(--border)}\n.op-sb-axis b{font-weight:400;color:var(--muted)}\n@keyframes op-sb-ping{0%{opacity:.55;transform:scale(1)}75%,100%{opacity:0;transform:scale(2.2)}}\n@media (prefers-reduced-motion:reduce){.op-sb-ping{animation:none}.op-sb-bar,.op-sb-tip{transition:none}}",
        "js": "(function(){\nvar DAY=864e5,WORD={up:'Operational',degraded:'Degraded',down:'Outage',none:'No data'},CUR={up:'Operational',degraded:'Degraded performance',down:'Outage',none:'No data'};\nfunction iso(t){return new Date(t).toISOString().slice(0,10)}\nfunction parse(s){return Date.UTC(+s.slice(0,4),+s.slice(5,7)-1,+s.slice(8,10))}\nfunction status(d){if(d.status)return d.status;if((d.downtime||0)>0)return 'down';if((d.degraded||0)>0)return 'degraded';return 'up'}\nfunction up(d){return d.uptime!=null?d.uptime:Math.max(0,100-((d.downtime||0)/1440)*100)}\nfunction dur(m){return m>=60?Math.floor(m/60)+'h'+(m%60?' '+(m%60)+'m':''):Math.max(1,Math.round(m))+'m'}\nvar loc=Intl.DateTimeFormat().resolvedOptions().locale;\nvar fShort=new Intl.DateTimeFormat(loc,{weekday:'short',month:'short',day:'numeric',timeZone:'UTC'}),fAxis=new Intl.DateTimeFormat(loc,{month:'short',day:'numeric',timeZone:'UTC'}),fLong=new Intl.DateTimeFormat(loc,{weekday:'long',month:'long',day:'numeric',timeZone:'UTC'}),fPct=new Intl.NumberFormat(loc,{maximumFractionDigits:2});\nfunction pct(v){return fPct.format(Math.floor(v*100)/100)+'%'}\ndocument.querySelectorAll('.op-status-bars').forEach(function(root){\n  var label=root.dataset.label,today=parse(iso(Date.now())),inc={};\n  JSON.parse(root.dataset.incidents||'[]').forEach(function(i){var d=iso(today-i.ago*DAY);inc[d]={date:d,degraded:i.degraded,downtime:i.downtime,incidents:[{title:i.title,minutes:i.degraded||i.downtime}]}});\n  var days=[];for(var t=today-89*DAY;t<=today;t+=DAY){var d=iso(t);days.push(inc[d]||{date:d})}\n  var now=status(days[days.length-1]);\n  root.innerHTML='<div class=\"op-sb-head\"><span class=\"op-sb-label\"></span><span class=\"op-sb-now\"><span class=\"op-sb-dotwrap\" aria-hidden=\"true\">'+((now==='down'||now==='degraded')?'<span class=\"op-sb-ping d-'+now+'\"></span>':'')+'<span class=\"op-sb-dot d-'+now+'\"></span></span>'+CUR[now]+'</span></div><div class=\"op-sb-wrap\"><div class=\"op-sb-strip\" role=\"grid\" tabindex=\"0\"></div><div class=\"op-sb-tip\" aria-hidden=\"true\"><div class=\"op-sb-tip-inner\"><div class=\"op-sb-card\"></div></div></div></div><div class=\"op-sb-axis\"><span class=\"a1\"></span><i></i><b class=\"a2\"></b><i></i><span>Today</span></div>';\n  root.querySelector('.op-sb-label').textContent=label;\n  var wrap=root.querySelector('.op-sb-wrap'),strip=root.querySelector('.op-sb-strip'),tip=root.querySelector('.op-sb-tip'),tipIn=root.querySelector('.op-sb-tip-inner'),card=root.querySelector('.op-sb-card');\n  var shown=[],active=null,pinned=false,kb=false,tx=0,cur=0,vel=0,raf=0,visible=false,width=0,gap=2;\n  function render(){\n    width=wrap.clientWidth;var fits=width?Math.floor((width+gap)/(3+gap)):90;var max=days.length;\n    var count=[max,60,30].filter(function(n){return n<=max}).find(function(n){return n<=fits})||Math.max(1,fits);\n    var tail=days.slice(-count);shown=[];for(var i=0;i<count-tail.length;i++)shown.push({date:'',status:'none'});shown=shown.concat(tail);\n    strip.innerHTML='';shown.forEach(function(d,i){var b=document.createElement('div');var s=d.date?status(d):'none';b.className='op-sb-bar s-'+s;b.setAttribute('role','gridcell');b.setAttribute('aria-label',describe(d));strip.appendChild(b)});\n    var m=shown.filter(function(d){return d.date&&status(d)!=='none'}),u=m.length?m.reduce(function(a,d){return a+up(d)},0)/m.length:null;\n    strip.setAttribute('aria-label',label+', last '+count+' days'+(u!==null?', '+pct(u)+' uptime':''));\n    root.querySelector('.a1').textContent=shown[0].date?fAxis.format(parse(shown[0].date)):count+' days ago';\n    root.querySelector('.a2').textContent=u===null?'No data yet':pct(u)+' uptime';\n  }\n  function describe(d){if(!d.date)return 'No data';var s=status(d),p=[fLong.format(parse(d.date)),WORD[s]];if(s!=='none')p.push(pct(up(d))+' uptime');(d.incidents||[]).forEach(function(i){p.push(i.minutes?i.title+' for '+dur(i.minutes):i.title)});return p.join(', ')}\n  function paintTip(){var sh=-Math.min(1,Math.max(0,cur/(width||1)))*100;tip.style.transform='translateX('+cur+'px)';tipIn.style.transform='translate('+sh+'%,-100%)'}\n  function spring(){var k=520,c=42,m=.6,dt=1/60;var a=(-k*(cur-tx)-c*vel)/m;vel+=a*dt;cur+=vel*dt;paintTip();if(Math.abs(cur-tx)>.1||Math.abs(vel)>.1)raf=requestAnimationFrame(spring);else{cur=tx;paintTip();raf=0}}\n  function point(i,instant){tx=((i+.5)*(width+gap))/shown.length-gap/2;if(instant||!visible||matchMedia('(prefers-reduced-motion: reduce)').matches){cur=tx;vel=0;paintTip()}else if(!raf)raf=requestAnimationFrame(spring);visible=true;setActive(i)}\n  function setActive(i){if(active!==null&&strip.children[active])strip.children[active].removeAttribute('data-active');active=i;\n    if(i===null){strip.removeAttribute('data-hovering');tip.removeAttribute('data-show');return}\n    strip.setAttribute('data-hovering','');strip.children[i].setAttribute('data-active','');\n    var d=shown[i],s=d.date?status(d):'none',h='<div class=\"op-sb-row\"><span style=\"font-weight:500\">'+(d.date?fShort.format(parse(d.date)):'Before tracking')+'</span>'+(s!=='none'?'<span class=\"op-sb-pct\">'+pct(up(d))+'</span>':'')+'</div><div class=\"op-sb-st\"><span class=\"op-sb-dot d-'+s+'\" style=\"flex-shrink:0\"></span>'+WORD[s]+'</div>';\n    if(s==='up'&&!(d.incidents||[]).length)h+='<div class=\"op-sb-sub\" style=\"margin-top:4px\">No incidents</div>';\n    (d.incidents||[]).forEach(function(x){h+='<div class=\"op-sb-inc\">'+x.title.replace(/</g,'&lt;')+(x.minutes?'<span class=\"op-sb-sub\"> · '+dur(x.minutes)+'</span>':'')+'</div>'});\n    card.innerHTML=h;tip.setAttribute('data-show','');}\n  function hide(){visible=false;setActive(null);pinned=false;kb=false}\n  function idx(e){var r=strip.getBoundingClientRect();return Math.min(shown.length-1,Math.max(0,Math.floor(((e.clientX-r.left)/r.width)*shown.length)))}\n  strip.addEventListener('pointermove',function(e){if(e.pointerType!=='mouse'||pinned)return;var i=idx(e);if(i!==active)point(i,false)});\n  strip.addEventListener('pointerdown',function(e){if(e.pointerType==='mouse')return;var i=idx(e);if(pinned&&i===active)return hide();point(i,true);pinned=true});\n  wrap.addEventListener('pointerleave',function(e){if(e.pointerType==='mouse'&&!pinned&&!kb)hide()});\n  document.addEventListener('pointerdown',function(e){if(pinned&&!wrap.contains(e.target)){visible=false;setActive(null);pinned=false}});\n  strip.addEventListener('focus',function(){if(!strip.matches(':focus-visible'))return;kb=true;point(active!=null?active:shown.length-1,true)});\n  strip.addEventListener('blur',hide);\n  strip.addEventListener('keydown',function(e){if(e.key==='Escape')return hide();var l=shown.length-1,f=active!=null?active:l,to={ArrowLeft:Math.max(0,f-1),ArrowRight:Math.min(l,f+1),Home:0,End:l,PageUp:Math.max(0,f-7),PageDown:Math.min(l,f+7)}[e.key];if(to==null)return;e.preventDefault();kb=true;point(to,true)});\n  new ResizeObserver(function(){var a=active;render();if(a!=null&&a<shown.length)point(a,true)}).observe(wrap);\n});\n})();",
        "react": "",
        "dependencies": [],
        "install": "",
        "licence_notice": ""
    }
}