# Status Bars

> Ninety-day uptime strip with a gliding tooltip, incident details and keyboard navigation.

- Kind: Element
- Page: https://ownlyplans.com/bhaskar/status-bars/
- Licence: MIT (do anything, keep the notice)

## Code

`index.html`

```html
<!--
  Ported to plain HTML/CSS/JS for OwnlyPlans from ObsidianUI (https://github.com/Atharvsinh-codez/ObsidianUI)
  MIT License — Copyright (c) 2026 ObsidianUI
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to the following conditions:
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
-->
<div class="op-sb-stage">
  <div class="op-sb-list">
    <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>
    <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>
  </div>
</div>
```

`styles.css`

```css
/* Ported from ObsidianUI (MIT License, Copyright (c) 2026 ObsidianUI) */
.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}
.op-sb-list{width:100%;max-width:640px;display:flex;flex-direction:column;gap:2rem}
.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}
.op-sb-head{margin-bottom:10px;display:flex;align-items:center;justify-content:space-between;gap:12px}
.op-sb-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;letter-spacing:-.01em}
.op-sb-now{display:flex;flex-shrink:0;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
.op-sb-dotwrap{position:relative;display:grid;width:8px;height:8px;place-items:center}
.op-sb-dot{width:6px;height:6px;border-radius:999px}
.op-sb-ping{position:absolute;inset:0;border-radius:999px;animation:op-sb-ping 1.8s cubic-bezier(0,0,.2,1) infinite}
.op-sb-wrap{position:relative}
.op-sb-strip{display:flex;height:32px;gap:2px;border-radius:3px;outline:none}
.op-sb-strip:focus-visible{outline:1px solid #a3a3a3;outline-offset:3px}
.op-sb-bar{min-width:0;flex:1;transform-origin:bottom;border-radius:2px;transition:opacity 150ms ease-out,transform 150ms ease-out}
.op-sb-strip[data-hovering] .op-sb-bar{opacity:.35}
.op-sb-strip[data-hovering] .op-sb-bar[data-active]{opacity:1;transform:scaleY(1.08)}
.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)}
.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)}
.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}
.op-sb-tip[data-show]{opacity:1;translate:0 0}
.op-sb-tip-inner{transform:translateY(-100%);padding-bottom:10px}
.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)}
.op-sb-row{display:flex;align-items:baseline;justify-content:space-between;gap:16px}
.op-sb-pct{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-variant-numeric:tabular-nums;color:var(--muted)}
.op-sb-st{margin-top:4px;display:flex;align-items:center;gap:6px;color:var(--muted)}
.op-sb-sub{color:color-mix(in oklab,var(--muted) 80%,transparent)}
.op-sb-inc{margin-top:6px;border-top:1px solid var(--border);padding-top:6px;color:var(--muted)}
.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)}
.op-sb-axis span{flex-shrink:0;font-variant-numeric:tabular-nums}.op-sb-axis i{height:1px;flex:1;background:var(--border)}
.op-sb-axis b{font-weight:400;color:var(--muted)}
@keyframes op-sb-ping{0%{opacity:.55;transform:scale(1)}75%,100%{opacity:0;transform:scale(2.2)}}
@media (prefers-reduced-motion:reduce){.op-sb-ping{animation:none}.op-sb-bar,.op-sb-tip{transition:none}}
```

`script.js`

```js
(function(){
var DAY=864e5,WORD={up:'Operational',degraded:'Degraded',down:'Outage',none:'No data'},CUR={up:'Operational',degraded:'Degraded performance',down:'Outage',none:'No data'};
function iso(t){return new Date(t).toISOString().slice(0,10)}
function parse(s){return Date.UTC(+s.slice(0,4),+s.slice(5,7)-1,+s.slice(8,10))}
function status(d){if(d.status)return d.status;if((d.downtime||0)>0)return 'down';if((d.degraded||0)>0)return 'degraded';return 'up'}
function up(d){return d.uptime!=null?d.uptime:Math.max(0,100-((d.downtime||0)/1440)*100)}
function dur(m){return m>=60?Math.floor(m/60)+'h'+(m%60?' '+(m%60)+'m':''):Math.max(1,Math.round(m))+'m'}
var loc=Intl.DateTimeFormat().resolvedOptions().locale;
var 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});
function pct(v){return fPct.format(Math.floor(v*100)/100)+'%'}
document.querySelectorAll('.op-status-bars').forEach(function(root){
  var label=root.dataset.label,today=parse(iso(Date.now())),inc={};
  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}]}});
  var days=[];for(var t=today-89*DAY;t<=today;t+=DAY){var d=iso(t);days.push(inc[d]||{date:d})}
  var now=status(days[days.length-1]);
  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>';
  root.querySelector('.op-sb-label').textContent=label;
  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');
  var shown=[],active=null,pinned=false,kb=false,tx=0,cur=0,vel=0,raf=0,visible=false,width=0,gap=2;
  function render(){
    width=wrap.clientWidth;var fits=width?Math.floor((width+gap)/(3+gap)):90;var max=days.length;
    var count=[max,60,30].filter(function(n){return n<=max}).find(function(n){return n<=fits})||Math.max(1,fits);
    var tail=days.slice(-count);shown=[];for(var i=0;i<count-tail.length;i++)shown.push({date:'',status:'none'});shown=shown.concat(tail);
    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)});
    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;
    strip.setAttribute('aria-label',label+', last '+count+' days'+(u!==null?', '+pct(u)+' uptime':''));
    root.querySelector('.a1').textContent=shown[0].date?fAxis.format(parse(shown[0].date)):count+' days ago';
    root.querySelector('.a2').textContent=u===null?'No data yet':pct(u)+' uptime';
  }
  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(', ')}
  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%)'}
  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}}
  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)}
  function setActive(i){if(active!==null&&strip.children[active])strip.children[active].removeAttribute('data-active');active=i;
    if(i===null){strip.removeAttribute('data-hovering');tip.removeAttribute('data-show');return}
    strip.setAttribute('data-hovering','');strip.children[i].setAttribute('data-active','');
    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>';
    if(s==='up'&&!(d.incidents||[]).length)h+='<div class="op-sb-sub" style="margin-top:4px">No incidents</div>';
    (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>'});
    card.innerHTML=h;tip.setAttribute('data-show','');}
  function hide(){visible=false;setActive(null);pinned=false;kb=false}
  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)))}
  strip.addEventListener('pointermove',function(e){if(e.pointerType!=='mouse'||pinned)return;var i=idx(e);if(i!==active)point(i,false)});
  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});
  wrap.addEventListener('pointerleave',function(e){if(e.pointerType==='mouse'&&!pinned&&!kb)hide()});
  document.addEventListener('pointerdown',function(e){if(pinned&&!wrap.contains(e.target)){visible=false;setActive(null);pinned=false}});
  strip.addEventListener('focus',function(){if(!strip.matches(':focus-visible'))return;kb=true;point(active!=null?active:shown.length-1,true)});
  strip.addEventListener('blur',hide);
  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)});
  new ResizeObserver(function(){var a=active;render();if(a!=null&&a<shown.length)point(a,true)}).observe(wrap);
});
})();
```

## About

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.

---
Also available as JSON: https://ownlyplans.com/bhaskar/status-bars/?format=json
MCP server: https://ownlyplans.com/mcp
