Data Display
Status Page
The page itself: one sentence at the top, and everything else under it.
import { StatusPage } from 'omaris' Examples
Basic
The banner is the whole design: someone opening a status page has one question and about two seconds of patience for it, so the answer is the largest thing on the screen, in a colour, with an icon that says the same thing for anyone the colour doesn't reach. worstStatus() computes it — the overall state of a system is its worst part, never its average.
All systems operational
Checked Sep 8, 12:00 PM
REST and GraphQL endpoints
app.example.com
<script lang="ts"> import { StatusPage, StatusService, worstStatus, type StatusPoint } from 'omaris'; function history(seed: number, blips: Record<number, StatusPoint['level']> = {}): StatusPoint[] { const today = new Date('2026-09-08T12:00:00Z'); return Array.from({ length: 90 }, (_, index) => { const date = new Date(today.getTime() - (89 - index) * 86_400_000); const level = blips[index] ?? ((index * seed) % 53 === 0 ? 'degraded' : 'operational'); return { date, level, uptime: level === 'operational' ? 100 : 96.4 }; }); } const services = [ { name: 'API', description: 'REST and GraphQL endpoints', history: history(7) }, { name: 'Dashboard', description: 'app.example.com', history: history(11, { 84: 'degraded' }) }, { name: 'Webhooks', history: history(13) } ];</script><StatusPage level={worstStatus(services.map((service) => service.history.at(-1)!.level))} updatedAt={new Date('2026-09-08T12:00:00Z')} class="w-full"> <div class="flex flex-col rounded-shape-lg border border-border px-4"> {#each services as service (service.name)} <StatusService {...service} class="border-b border-border last:border-b-0" /> {/each} </div></StatusPage> Uptime history
A status page that shows only today answers "is it working now" and nothing else; the bar answers "is it usually working", which is the question anyone reading one actually has. How many days fit is measured rather than guessed — the track draws as many bars as it can at a readable width and drops the oldest — so the same row is ninety days wide and three weeks narrow with no breakpoint anywhere. Hover a bar for the day.
Outbound delivery
<script lang="ts"> import { StatusService, type StatusPoint } from 'omaris'; const today = new Date('2026-09-08T12:00:00Z'); const bad: Record<number, StatusPoint['level']> = { 61: 'degraded', 86: 'major', 87: 'partial' }; const history: StatusPoint[] = Array.from({ length: 90 }, (_, index) => { const date = new Date(today.getTime() - (89 - index) * 86_400_000); const level = bad[index] ?? 'operational'; return { date, level, uptime: level === 'operational' ? 100 : level === 'major' ? 21.4 : 96.8, note: level === 'operational' ? undefined : 'Elevated error rates in eu-west' }; });</script><div class="flex w-full flex-col rounded-shape-lg border border-border px-4"> <StatusService name="Webhooks" description="Outbound delivery" {history} class="border-b border-border" /> <StatusService name="Documentation" history={history.map((day) => ({ ...day, level: 'operational', uptime: 100 }))} density="compact" /></div> Incidents
Newest first, because the question is always "what is happening now" and the answer should not be at the bottom of a scroll. The older updates stay, in order, because the second question is "how long has this been going on" — and a page that overwrites its own history every twenty minutes cannot answer either one afterwards. The badge follows the most recent update.
Elevated error rates on the API
Identified Sep 8, 11:40 AM- Identified Sep 8, 12:02 PM
A connection pool exhausted after a bad deploy. Rolling back.
- Investigating Sep 8, 11:40 AM
We are seeing a rise in 5xx responses on the API and are looking into it.
Webhook delivery delayed in eu-west
Resolved Sep 6, 08:12 AM- Resolved Sep 6, 10:05 AM
The queue is empty. Every delayed webhook was sent.
- Monitoring Sep 6, 09:20 AM
The rollback is out and the backlog is draining.
- Investigating Sep 6, 08:12 AM
A backlog is building on the eu-west delivery queue.
<script lang="ts"> import { StatusIncident } from 'omaris';</script><div class="flex w-full flex-col gap-8"> <StatusIncident title="Elevated error rates on the API" level="major" startedAt={new Date('2026-09-08T11:40:00Z')} services={['API', 'Dashboard']} updates={[ { at: new Date('2026-09-08T11:40:00Z'), stage: 'investigating', body: 'We are seeing a rise in 5xx responses on the API and are looking into it.' }, { at: new Date('2026-09-08T12:02:00Z'), stage: 'identified', body: 'A connection pool exhausted after a bad deploy. Rolling back.' } ]} /> <StatusIncident title="Webhook delivery delayed in eu-west" level="partial" startedAt={new Date('2026-09-06T08:12:00Z')} services={['Webhooks']} muted updates={[ { at: new Date('2026-09-06T08:12:00Z'), stage: 'investigating', body: 'A backlog is building on the eu-west delivery queue.' }, { at: new Date('2026-09-06T09:20:00Z'), stage: 'monitoring', body: 'The rollback is out and the backlog is draining.' }, { at: new Date('2026-09-06T10:05:00Z'), stage: 'resolved', body: 'The queue is empty. Every delayed webhook was sent.' } ]} /></div> Emphasis
emphasis is how loudly the banner is drawn: tonal for a status page, solid for a wallboard across a room, quiet for a strip inside an app that is not about status. StatusIndicator is the same state at dot size — the label ships by default, because a page whose whole message is a colour has no message for a good share of the people looking at it.
Major system outage
Checked Sep 8, 12:00 PM
Degraded performance
Checked Sep 8, 12:00 PM
All systems operational
Live
<script lang="ts"> import { StatusIndicator, StatusPage } from 'omaris';</script><div class="flex w-full flex-col gap-3"> <StatusPage level="major" emphasis="solid" updatedAt={new Date('2026-09-08T12:00:00Z')} /> <StatusPage level="degraded" emphasis="tonal" updatedAt={new Date('2026-09-08T12:00:00Z')} /> <StatusPage level="operational" emphasis="quiet" updatedLabel="Live" /> <div class="flex flex-wrap items-center gap-5 pt-1"> <StatusIndicator level="operational" /> <StatusIndicator level="degraded" pulse /> <StatusIndicator level="major" label="us-east-1" pulse /> <StatusIndicator level="maintenance" size="sm" /> </div></div> Overridden
Every part is reachable through classes: the banner's radius, the icon disc, the height of the bars and their corners. class still covers the root, and title and updatedLabel replace the sentences the level would otherwise write.
Two regions are slow
Updated a moment ago
<script lang="ts"> import { StatusPage, StatusService, type StatusPoint } from 'omaris'; const today = new Date('2026-09-08T12:00:00Z'); const history: StatusPoint[] = Array.from({ length: 90 }, (_, index) => { const date = new Date(today.getTime() - (89 - index) * 86_400_000); const level: StatusPoint['level'] = index >= 88 ? 'degraded' : 'operational'; return { date, level, uptime: level === 'operational' ? 100 : 97.2 }; });</script><StatusPage level="degraded" title="Two regions are slow" updatedLabel="Updated a moment ago" class="w-full gap-3" classes={{ banner: 'rounded-shape-sm', mark: 'rounded-shape-sm' }}> <StatusService name="eu-west" level="degraded" {history} legend="3 months ago" classes={{ track: 'h-12', bar: 'rounded-shape-xs' }} /></StatusPage> When to use it
Use it for
- The page people open when something looks broken: one sentence at the top, the services under it, and what happened recently.
- A per-service uptime history, the ninety-day bar. It answers "is it usually working".
- An incident and its thread.
StatusIncidentkeeps every update in order and badges the incident with the latest one. - A status strip inside an app that is not about status:
emphasis="quiet", orStatusIndicatoron its own beside a region, a queue or a host. - A wallboard:
emphasis="solid"is the accent at full strength, readable across a room.
Not for
- A metric over time, like latency or error rate → Chart. The bar here is categorical: each day is a state, not a number.
- One thing's state inline in a table or list → a Badge or a Chip.
StatusIndicatoris for a row where the state is the point. - Progress through a task you started → Progress.
- Telling someone their own action failed → Toast or Alert. This page is about the system.
Do
- Compute the banner with
worstStatus(). The overall state of a system is its worst part. - Always pass
updatedAt(orupdatedLabel). A status page with no timestamp looks broken. - Give
historyoldest first. Days that do not fit are dropped from the front, so the most recent ones stay on screen. - Use the levels as meant:
degradedis slow,partialis some of it down,majoris all of it. - Write
noteon the bad days. It is what the hover readout shows. - Keep resolved incidents on the page,
muted. The history is the credibility.
Don't
- Rely on the dot alone.
StatusIndicatorships its label because green and amber are the same dot to many readers. - Mark planned work as an outage.
maintenanceis left out of the uptime sum on purpose. - Show ninety focusable bars. The track is one
role="img"with the row summarised. - Round uptime up to 100%.
formatUptimerefuses to, and so should whatever feeds it.
Quick reference
emphasis StatusPagetonal(default)solidquiet
How loudly the banner is drawn.
size StatusIndicatorsmmd(default)lg
density StatusServicecomfortable(default)compact
API
StatusPage
The page itself: one sentence at the top, and everything else under it.
The banner is the whole design. Someone opening a status page has one question and about two seconds of patience for it, so the answer is the largest thing on the screen, in a colour, with an icon that says the same thing for anyone the colour doesn't reach — and worstStatus() computes it, because the overall state of a system is its worst part, never its average.
The time it was last checked sits under that. A status page with no timestamp is a status page nobody can tell from a broken one.
import { StatusPage } from 'omaris' <StatusPage level={worstStatus(services.map((s) => s.level))} updatedAt={new Date()}> {#each services as service} <StatusService {...service} /> {/each}</StatusPage> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
banner- The headline panel.
mark- The icon disc.
headline- No description in the source yet.
title- No description in the source yet.
updated- "Checked 2 minutes ago".
actions- Buttons on the right of the banner.
section- A section — the services, the incident history.
sectionHeader- No description in the source yet.
sectionTitle- No description in the source yet.
group- The stack of services, with a hairline between rows.
timeline- The stack of incidents.
empty- Shown where a section has nothing in it — the good outcome, usually.
Props
level required StatusLevel The one thing the page says. worstStatus() computes it from the parts.
title string Replaces the sentence the level would otherwise write.
updatedAt Date | string When the numbers were last true.
updatedLabel string Replaces "Checked <time>".
emphasis Defaults to 'tonal'
StatusPageEmphasis tonal- Tinted panel. The default: readable across a room.
solid- The accent at full strength — for a wallboard.
quiet- A hairline and a dot. For a status strip inside an app.
locale string actions Snippet Buttons on the right of the banner — subscribe, refresh, history.
banner Snippet Replaces the whole banner.
children Snippet Everything under it: services, incidents, whatever else.
class string classes StatusPageClasses Per-part Tailwind overrides. class still covers the root.
StatusIncident
One incident, and the thread of updates under it.
Newest first, because the question is always "what is happening now" and the answer to it should not be at the bottom of a scroll. The older updates stay, in order, because the second question is "how long has this been going on" — and a status page that overwrites its own history every twenty minutes cannot answer either one afterwards.
import { StatusIncident } from 'omaris' Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
header- No description in the source yet.
titlebasisso the row actually wraps.flex-1alone is a basis of zero, so on a phone the badge and the timestamp keep their width and the headline takes what is left — which was 130px, and an incident title read three words down a column. Below 12rem the two of them drop to a line of their own instead.stage- The badge saying how far along it is.
when- No description in the source yet.
scope- The affected services, listed under the title.
chip- No description in the source yet.
updates- The thread. The rule down the left is drawn by the items.
update- One update. The line and the node are
before/afteron the item itself, so the thread is one element per update rather than an element per update plus an element per connector. updateHead- No description in the source yet.
updateStage- No description in the source yet.
updateWhen- No description in the source yet.
body- No description in the source yet.
Props
title required string What happened, in a line.
level Defaults to 'major'
StatusLevel How bad it was. Colours the title's marker.
startedAt Date | string When it started.
services Defaults to []
string[] What it affected.
updates Defaults to []
IncidentUpdate[] The thread. Given oldest first or newest first; it is sorted.
muted Defaults to false
boolean Dim it — for a resolved incident in a history list.
locale string children Snippet Replaces the update thread.
class string classes StatusIncidentClasses Per-part Tailwind overrides. class still covers the root.
StatusIndicator
A dot, and what it means.
The dot alone is the thing everybody draws and nobody can read: green and amber are the same dot to a good share of people looking at it, and a page whose entire message is a colour has no message for them. So the label ships by default and label={false} is the deliberate choice, with the state still readable to a screen reader either way.
A live state pulses. Not for decoration — a page left open on a wall is the normal way this is read, and the pulse is what says the page is still watching rather than frozen on an old render.
import { StatusIndicator } from 'omaris' Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
dot- The dot itself, with a halo that beats out when
pulseis on. label- No description in the source yet.
Props
level required StatusLevel label string | false The words beside the dot. false hides them, a string replaces them.
size Defaults to 'md'
StatusIndicatorSize smmdlg
pulse Defaults to false
boolean Beat the halo — for a page that is watching a live feed.
class string classes StatusIndicatorClasses StatusService
One service, and the last however many days of it.
The bar is the part that matters. A status page that shows only today answers "is it working now" and nothing else; the history is what answers "is it usually working", which is the question anyone reading a status page actually has.
How many days fit is measured, not guessed: the track counts how many bars it can draw at a readable width and shows that many of the most recent, so the same component is ninety days on a dashboard and three weeks on a phone without a breakpoint anywhere.
import { StatusService } from 'omaris' <StatusService name="API" level="operational" history={days} /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
header- Name on the left, current state on the right.
name- No description in the source yet.
description- No description in the source yet.
state- No description in the source yet.
track- The row of bars.
bar- One day.
readout- The hovered day's detail, floating over the track.
legend- "90 days ago — 99.98% uptime — Today".
legendRule- No description in the source yet.
uptime- No description in the source yet.
Props
name required string What the service is called.
description string A line under it — what it covers, or which region.
level StatusLevel How it is right now. Defaults to the most recent point's level.
history Defaults to []
StatusPoint[] Oldest first. Anything that does not fit is dropped from the front.
uptime number Uptime to print, 0–100. Computed from history when left out — pass it when the real number comes from somewhere with more data than the bars are showing.
legend string | false The words under the bar. false drops the whole legend row.
labels { state?: string | false; uptime?: string; today?: string } The words the row writes for itself, for a page that is not in English: the state beside the dot, and the two fixed words in the legend. One prop rather than three, because they are always replaced together — a row that says "جاهزية" and "Today" is worse than either. state: false leaves the dot alone, with the state still read out to a screen reader.
minBarWidth Defaults to 4
number How thin the bars may get before older days are dropped, in px.
density Defaults to 'comfortable'
StatusServiceDensity comfortablecompact
locale string Locale for the dates and the percentage.
children Snippet Extra content under the legend — a link to the region's own page.
class string classes StatusServiceClasses Per-part Tailwind overrides. class still covers the root.