More Dashboards Snippets
Uptime Status Page — Service History Bars HTML CSS
Uptime Status Page · Dashboards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Uptime Status Page — 90-Day History Bars, Hover Tooltips & Aggregated Status

A public status page exists to answer one question fast — "is it just me, or is the whole service down?" — and the format every major status page (Statuspage, Better Uptime, Cachet) converges on is the same: a banner summarizing overall health, and a row of tiny colored bars per service showing recent history at a glance. This snippet builds that exact pattern in plain HTML, CSS, and vanilla JavaScript.
History bars as data, not images
Each service renders 90 thin <div> bars in a flex row, one per day, colored green/amber/red for operational/degraded/outage. genHistory() generates a randomized history that's mostly green with rare amber and red days, and the *last* bar always reflects the service's actual current status — so the simulated history stays consistent with the live status shown in the row above it. Replace genHistory() with real incident data and the rendering needs no changes.
Hover tooltips without a tooltip library
Each bar carries its day-relative label and status in a data-tip attribute, and a pure-CSS ::after pseudo-element reads it via content: attr(data-tip) on hover — no JavaScript positioning logic and no extra DOM nodes per bar, which matters here since there are 90 bars × however many services. This is the same zero-JS tooltip technique used for lightweight hint text throughout this library.
Uptime percentage, computed from the bars themselves
uptimePct() doesn't take a separate "99.9% uptime" number from anywhere — it counts how many of the 90 generated bars are operational and divides by the total. This guarantees the displayed percentage and the visual bar row can never contradict each other, which is a real bug class in hand-maintained status pages where the number and the history graphic are updated separately and drift apart.
Aggregated overall status
The top banner doesn't just say "operational" by default — updateOverall() reduces every service's status to find the *worst* one present (outage beats degraded beats operational) and reflects that exact severity in both the banner's color and its message. A single degraded service is enough to turn the whole banner amber; a single outage turns it red — exactly how users expect "is everything okay?" to be answered.
Why a status page exists separately from internal monitoring
Internal dashboards (Grafana, Datadog) are built for engineers diagnosing a problem; a public status page is built for everyone else asking one question fast. Keeping it as its own simple, low-dependency page — rather than exposing the internal monitoring tool directly — also means it can stay up and informative even during an incident that's affecting other parts of the infrastructure, which is exactly the moment a status page matters most.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Instead of working through the aggregation logic by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how updateOverall() uses the rank object to reduce five independent service statuses down to a single worst-case banner state, and why uptimePct() recomputes its percentage by counting the same bars array rather than storing a separately maintained number. It's also worth asking about the hover tooltips — have it explain why data-tip plus a CSS ::after avoids creating ninety extra DOM nodes per service, and whether that approach still holds up with many more services or a longer history window. For extending it, have it add a written incident log that links specific degraded/outage days to a description, a way to filter the service list, or an auto-refresh polling loop that re-fetches live status on an interval. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a public uptime status page in plain HTML, CSS, and vanilla JavaScript with no libraries.
Requirements:
- A list of services, each with a name and a current status of "operational", "degraded", or "outage".
- For each service, generate a 90-day history of daily statuses, mostly operational with rare degraded/outage days, but force the most recent (today's) entry to always match that service's actual current status so the generated history never contradicts the live status shown above it.
- Render each day of history as its own thin colored bar in a horizontal row (not as an image), colored per its status, with a tooltip that appears on hover showing that day's relative label (e.g. "12d ago") and status — implemented purely with a data attribute and a CSS ::after pseudo-element reading it via content: attr(), with no JavaScript-positioned tooltip element and no extra DOM node per bar.
- Compute each service's displayed uptime percentage by counting how many of its own 90 generated history bars are "operational" and dividing by the total — never from a separate hardcoded number — so the displayed percentage and the visual bar row can never disagree.
- Compute one aggregated overall status banner by reducing every service's status to find the single worst one present (outage ranks worse than degraded, which ranks worse than operational), and reflect that severity in both the banner's color and its message text.
- Make it possible to regenerate the entire page (all bars, percentages, and the banner) from a single function call after editing the underlying service list or statuses.Step by step
How to Use
- 1Paste HTML, CSS, and JSA status page renders with an overall banner and five services, each with a row of 90 colored history bars.
- 2Read the overall bannerIts color and message reflect the single worst status across every service — amber for any degraded service, red for any outage.
- 3Hover a history barA tooltip shows that day's relative date and status (e.g. "12d ago · Operational") without any extra markup per bar.
- 4Check the uptime percentageEach service's footer shows a percentage computed directly from its own visible 90-day bar history.
- 5Edit the servicesChange any entry's name or status in SERVICES, then call buildServices() to regenerate the whole page including the overall banner.
- 6Connect real incident dataReplace genHistory()'s random generator with your actual per-day status history fetched from an incident/monitoring API.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Replace genHistory()'s random-status loop with a fetch to your monitoring or incident-management API, mapping each returned day to 'operational', 'degraded', or 'outage'; keep the same array shape so buildServices() and uptimePct() need no other changes.
For days with a 'degraded' or 'outage' status, attach an incident summary string and render a list of those summaries (with dates) beneath the bar row or in a separate "Past incidents" section, linking each entry back to the relevant day.
Change the DAYS constant — genHistory(), the bar rendering, and uptimePct() all derive their loop length and percentage calculation from that single value.
Wrap a fetch-and-rebuild call in setInterval (e.g. every 60 seconds), updating each service's live status from your API before calling buildServices() again, and update the "Updated just now" label with the current timestamp.
In React, keep services in state and derive history/uptime with useMemo per service, rendering bars with .map(); in Vue, use a computed services array with nested v-for; in Angular, use *ngFor with a pipe for the uptime percentage. The worst-status reduction for the overall banner ports directly into each framework.