{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "status-1",
  "title": "Status 1: system status page",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A public status page: an overall banner worked out from the data, each service with a 90-day history you scrub by pointer or arrow keys, live incidents as a timeline of updates, two weeks of past incidents and a subscribe box.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/button.json",
    "https://ui.ballmac.com/r/input.json",
    "https://ui.ballmac.com/r/skeleton.json",
    "https://ui.ballmac.com/r/state-view.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/status-1/status-1.tsx",
      "content": "// Ballmac UI: Status 1. https://ui.ballmac.com/blocks/status-1\n\"use client\"\n\nimport * as React from \"react\"\nimport { CircleAlert, CircleCheck, CircleDashed, CircleX, Loader2, Mail, Wrench } from \"lucide-react\"\n\nimport { Button } from \"@/components/ballmac/button\"\nimport { Input } from \"@/components/ballmac/input\"\nimport { Skeleton } from \"@/components/ballmac/skeleton\"\nimport { frameProps, StateView, type StateProps } from \"@/components/ballmac/state-view\"\nimport { cn } from \"@/lib/utils\"\n\ntype Status1Level = \"operational\" | \"degraded\" | \"outage\" | \"maintenance\"\ntype Status1Day = \"ok\" | \"degraded\" | \"outage\" | \"none\"\n\ntype Status1Component = {\n  id: string\n  name: string\n  description?: string\n  /** Right now. */\n  status: Status1Level\n  /** One entry per day, oldest first, ending today. Ninety is the usual window. */\n  days: Status1Day[]\n}\n\ntype Status1Update = { status: \"investigating\" | \"identified\" | \"monitoring\" | \"resolved\" | \"scheduled\"; /** \"09:42\" */ time: string; body: string }\n\ntype Status1Incident = {\n  id: string\n  title: string\n  impact: \"minor\" | \"major\" | \"maintenance\"\n  /** ISO date the incident started. */\n  day: string\n  /** Names of the affected components. */\n  affects?: string[]\n  /** Newest first. The first one's status is the incident's status. */\n  updates: Status1Update[]\n}\n\ntype Status1Props = Omit<React.ComponentProps<\"section\">, \"title\"> &\n  StateProps & {\n    title?: string\n    components?: Status1Component[]\n    incidents?: Status1Incident[]\n    /** An ISO date for \"today\". The block never reads the clock. */\n    today?: string\n    /** \"10:42 UTC\", already written for people. */\n    updatedAt?: string\n    locale?: string\n    /** Called with the email to subscribe. Return a promise; reject to show its message. */\n    onSubscribe?: (email: string) => void | Promise<void>\n  }\n\nconst DAY = 86_400_000\nconst toT = (iso: string) => Date.parse(`${iso}T00:00:00Z`)\n\n// A believable 90 days: integer maths only, so the server and the browser draw the same bars.\nfunction history(seed: number, bad: Record<number, Status1Day> = {}): Status1Day[] {\n  return Array.from({ length: 90 }, (_, i) => bad[89 - i] ?? (((i * 37 + seed * 11) % 97 === 0 && i < 80) ? \"degraded\" : \"ok\"))\n}\n\nconst sampleComponents: Status1Component[] = [\n  { id: \"api\", name: \"API\", description: \"Requests to api.acme.com\", status: \"degraded\", days: history(1, { 0: \"degraded\", 17: \"outage\", 52: \"degraded\" }) },\n  { id: \"dash\", name: \"Dashboard\", description: \"The web app\", status: \"operational\", days: history(2, { 17: \"degraded\" }) },\n  { id: \"hooks\", name: \"Webhooks\", description: \"Event delivery\", status: \"operational\", days: history(3, { 33: \"degraded\", 34: \"degraded\" }) },\n  { id: \"auth\", name: \"Sign in\", description: \"Login, SSO and two-step codes\", status: \"operational\", days: history(4) },\n  { id: \"files\", name: \"File storage\", description: \"Uploads and downloads\", status: \"maintenance\", days: history(5, { 0: \"none\", 61: \"degraded\" }) },\n  { id: \"mail\", name: \"Email delivery\", description: \"Notifications and receipts\", status: \"operational\", days: history(6, { 8: \"outage\", 9: \"degraded\" }) },\n]\n\nconst sampleIncidents: Status1Incident[] = [\n  {\n    id: \"i3\",\n    title: \"Slower responses from the API\",\n    impact: \"minor\",\n    day: \"2026-10-18\",\n    affects: [\"API\"],\n    updates: [\n      { status: \"monitoring\", time: \"10:12\", body: \"A fix is live and response times are coming back down. We are watching it before we call it resolved.\" },\n      { status: \"identified\", time: \"09:40\", body: \"A busy database node is slowing some requests. We are moving its traffic to a healthy one.\" },\n      { status: \"investigating\", time: \"09:18\", body: \"Some requests are taking longer than usual. We are looking into it.\" },\n    ],\n  },\n  {\n    id: \"i2\",\n    title: \"File storage maintenance\",\n    impact: \"maintenance\",\n    day: \"2026-10-18\",\n    affects: [\"File storage\"],\n    updates: [{ status: \"scheduled\", time: \"08:00\", body: \"Uploads may pause for a few minutes while we move to faster disks. Downloads keep working.\" }],\n  },\n  {\n    id: \"i0\",\n    title: \"Emails sent late\",\n    impact: \"minor\",\n    day: \"2026-10-14\",\n    affects: [\"Email delivery\"],\n    updates: [\n      { status: \"resolved\", time: \"11:20\", body: \"The delay is over and every queued email has been sent.\" },\n      { status: \"investigating\", time: \"10:05\", body: \"Receipts and notifications are arriving up to an hour late.\" },\n    ],\n  },\n  {\n    id: \"i1\",\n    title: \"Webhook deliveries delayed\",\n    impact: \"major\",\n    day: \"2026-10-09\",\n    affects: [\"Webhooks\", \"API\"],\n    updates: [\n      { status: \"resolved\", time: \"14:55\", body: \"Deliveries are caught up. Every event was delivered; none were lost.\" },\n      { status: \"identified\", time: \"14:05\", body: \"A queue stalled after a deploy. We rolled the deploy back and are replaying the backlog.\" },\n      { status: \"investigating\", time: \"13:42\", body: \"Webhooks are arriving late. We are investigating.\" },\n    ],\n  },\n]\n\nconst levelMeta: Record<Status1Level, { label: string; icon: typeof CircleCheck; tone: string }> = {\n  operational: { label: \"Operational\", icon: CircleCheck, tone: \"text-chart-2\" },\n  degraded: { label: \"Degraded\", icon: CircleAlert, tone: \"text-amber-600 dark:text-amber-400\" },\n  outage: { label: \"Outage\", icon: CircleX, tone: \"text-destructive\" },\n  maintenance: { label: \"Maintenance\", icon: Wrench, tone: \"text-chart-1\" },\n}\nconst dayMeta: Record<Status1Day, { label: string; bar: string }> = {\n  ok: { label: \"No issues\", bar: \"bg-chart-2\" },\n  degraded: { label: \"Degraded\", bar: \"bg-amber-400 dark:bg-amber-500\" },\n  outage: { label: \"Outage\", bar: \"bg-destructive\" },\n  none: { label: \"No data\", bar: \"bg-muted-foreground/25\" },\n}\nconst stepLabel: Record<Status1Update[\"status\"], string> = { investigating: \"Investigating\", identified: \"Identified\", monitoring: \"Monitoring\", resolved: \"Resolved\", scheduled: \"Scheduled\" }\nconst impactLabel = { minor: \"Minor\", major: \"Major\", maintenance: \"Maintenance\" }\n\nfunction Skel() {\n  return (\n    <div aria-hidden=\"true\" className=\"grid gap-6\">\n      <Skeleton className=\"h-24 rounded-2xl\" />\n      <div className=\"rounded-2xl border p-6\">\n        {[0, 1, 2, 3].map((i) => (\n          <div key={i} className=\"py-4 first:pt-0 last:pb-0\">\n            <Skeleton className=\"h-4 w-32\" />\n            <Skeleton className=\"mt-3 h-8 w-full\" />\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nfunction UptimeBar({ item, today, locale }: { item: Status1Component; today: string; locale: string }) {\n  const fmt = React.useMemo(() => new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", year: \"numeric\", timeZone: \"UTC\" }), [locale])\n  const [at, setAt] = React.useState<number | null>(null)\n  const ref = React.useRef<HTMLDivElement>(null)\n  const n = item.days.length\n  const dateOf = (i: number) => fmt.format(toT(today) - (n - 1 - i) * DAY)\n  const counts = { outage: item.days.filter((d) => d === \"outage\").length, degraded: item.days.filter((d) => d === \"degraded\").length }\n  const known = item.days.filter((d) => d !== \"none\").length\n  // A day with an outage counts as down; a degraded day counts as half.\n  const uptime = known ? (100 * (known - counts.outage - counts.degraded * 0.5)) / known : 100\n  const summary = `${uptime.toFixed(2)}% uptime over ${n} days: ${counts.outage} ${counts.outage === 1 ? \"day\" : \"days\"} with an outage, ${counts.degraded} degraded`\n  const move = (e: React.KeyboardEvent) => {\n    const cur = at ?? n - 1\n    const next = e.key === \"ArrowLeft\" ? cur - 1 : e.key === \"ArrowRight\" ? cur + 1 : e.key === \"Home\" ? 0 : e.key === \"End\" ? n - 1 : null\n    if (next === null) return\n    e.preventDefault()\n    setAt(Math.max(0, Math.min(n - 1, next)))\n  }\n  return (\n    <div>\n      <div\n        ref={ref}\n        dir=\"ltr\"\n        role=\"slider\"\n        tabIndex={0}\n        aria-label={`${item.name} history, one bar per day`}\n        aria-valuemin={0}\n        aria-valuemax={n - 1}\n        aria-valuenow={at ?? n - 1}\n        aria-valuetext={at === null ? summary : `${dateOf(at)}: ${dayMeta[item.days[at]!].label}`}\n        onKeyDown={move}\n        onFocus={() => setAt((a) => a ?? n - 1)}\n        onBlur={() => setAt(null)}\n        onPointerLeave={() => setAt(null)}\n        onPointerMove={(e) => {\n          const r = ref.current!.getBoundingClientRect()\n          setAt(Math.max(0, Math.min(n - 1, Math.floor(((e.clientX - r.left) / r.width) * n))))\n        }}\n        className=\"focus-visible:ring-ring/50 relative flex h-9 cursor-crosshair items-stretch gap-[2px] rounded-md outline-none focus-visible:ring-[3px] sm:gap-[3px]\"\n      >\n        {item.days.map((d, i) => (\n          <span key={i} aria-hidden=\"true\" className={cn(\"min-w-0 flex-1 rounded-[2px] transition-[opacity,transform] duration-100 motion-reduce:transition-none\", dayMeta[d].bar, at !== null && at !== i && \"opacity-60\", at === i && \"scale-y-110\")} />\n        ))}\n        {at !== null ? (\n          <span\n            aria-hidden=\"true\"\n            className=\"bg-popover text-popover-foreground pointer-events-none absolute -top-11 z-10 -translate-x-1/2 rounded-md border px-2.5 py-1.5 text-xs whitespace-nowrap shadow-md\"\n            style={{ left: `clamp(4.5rem, ${((at + 0.5) / n) * 100}%, calc(100% - 4.5rem))` }}\n          >\n            <span className=\"font-medium\">{dateOf(at)}</span> <span className=\"text-muted-foreground\">· {dayMeta[item.days[at]!].label}</span>\n          </span>\n        ) : null}\n      </div>\n      <div aria-hidden=\"true\" dir=\"ltr\" className=\"text-muted-foreground mt-1.5 flex justify-between text-xs\">\n        <span>{n} days ago</span>\n        <span>{uptime.toFixed(2)}% uptime</span>\n        <span>Today</span>\n      </div>\n      <p className=\"sr-only\">{summary}</p>\n    </div>\n  )\n}\n\n/**\n * A public status page: one banner that says how things are overall, each service with a 90-day history you can scrub with a pointer or\n * the arrow keys, what is happening now as a timeline of updates, the last two weeks of incidents, and a subscribe box. The overall\n * message and every uptime figure are worked out from the data you pass.\n */\nfunction Status1({\n  title = \"Acme status\",\n  components = sampleComponents,\n  incidents = sampleIncidents,\n  today = \"2026-10-18\",\n  updatedAt = \"10:42 UTC\",\n  locale = \"en\",\n  onSubscribe,\n  state = \"ready\",\n  onRetry,\n  emptyState,\n  errorState,\n  className,\n  ...props\n}: Status1Props) {\n  const id = React.useId()\n  const list = state === \"empty\" ? [] : components\n  const active = incidents.filter((i) => i.updates[0]?.status !== \"resolved\")\n  const level: Status1Level = list.some((c) => c.status === \"outage\") ? \"outage\" : list.some((c) => c.status === \"degraded\") ? \"degraded\" : list.some((c) => c.status === \"maintenance\") ? \"maintenance\" : \"operational\"\n  const overall = {\n    operational: { text: \"All systems operational\", sub: \"Everything is running normally.\", box: \"bg-chart-2/10 border-chart-2/30\" },\n    degraded: { text: \"Some systems are slower than usual\", sub: \"We know and we are working on it.\", box: \"bg-amber-500/10 border-amber-500/40\" },\n    outage: { text: \"Some systems are down\", sub: \"We are working on it right now.\", box: \"bg-destructive/10 border-destructive/30\" },\n    maintenance: { text: \"Scheduled maintenance in progress\", sub: \"Some things may pause for a short while.\", box: \"bg-chart-1/10 border-chart-1/30\" },\n  }[level]\n  const L = levelMeta[level]\n  const dayFmt = React.useMemo(() => new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"long\", timeZone: \"UTC\" }), [locale])\n\n  const [email, setEmail] = React.useState(\"\")\n  const [sub, setSub] = React.useState<\"idle\" | \"sending\" | \"done\">(\"idle\")\n  const [subError, setSubError] = React.useState<string>()\n  async function subscribe(e: React.FormEvent) {\n    e.preventDefault()\n    if (!/^\\S+@\\S+\\.\\S+$/.test(email.trim())) return setSubError(\"Enter an email address like name@company.com.\")\n    setSubError(undefined)\n    setSub(\"sending\")\n    try {\n      await onSubscribe?.(email.trim())\n      setSub(\"done\")\n    } catch (err) {\n      setSub(\"idle\")\n      setSubError(err instanceof Error ? err.message : \"We could not subscribe you. Try again.\")\n    }\n  }\n\n  // The last 14 days, newest first, each with its incidents (or the plain statement that there were none).\n  const days = Array.from({ length: 14 }, (_, i) => new Date(toT(today) - i * DAY).toISOString().slice(0, 10))\n  const past = incidents.filter((i) => i.updates[0]?.status === \"resolved\")\n  // Quiet days that follow each other share one row instead of a wall of \"nothing happened\".\n  const rows: { days: string[]; here: Status1Incident[] }[] = []\n  for (const d of days) {\n    const here = past.filter((i) => i.day === d)\n    const last = rows.at(-1)\n    if (!here.length && last && !last.here.length) last.days.push(d)\n    else rows.push({ days: [d], here })\n  }\n\n  const content = !list.length ? (\n    <div className=\"bg-card rounded-2xl border px-6 py-16 text-center\">\n      <span aria-hidden=\"true\" className=\"bg-muted mx-auto flex size-12 items-center justify-center rounded-full\">\n        <CircleDashed className=\"size-6\" />\n      </span>\n      <h2 className=\"mt-5 text-lg font-semibold tracking-tight\">{emptyState?.title ?? \"No services are listed yet\"}</h2>\n      <p className=\"text-muted-foreground mx-auto mt-1.5 max-w-xs text-sm text-pretty\">{emptyState?.description ?? \"Once services are added, their status and history appear here.\"}</p>\n      {emptyState?.action}\n    </div>\n  ) : (\n    <div className=\"grid gap-10\">\n      <div role=\"status\" className={cn(\"flex items-center gap-4 rounded-2xl border p-5 sm:p-6\", overall.box)}>\n        <L.icon aria-hidden=\"true\" className={cn(\"size-8 shrink-0\", L.tone)} />\n        <div className=\"min-w-0\">\n          <p className=\"text-xl font-semibold tracking-tight text-balance sm:text-2xl\">{overall.text}</p>\n          <p className=\"text-muted-foreground mt-0.5 text-sm\">\n            {overall.sub} Updated {updatedAt}.\n          </p>\n        </div>\n      </div>\n\n      {active.length ? (\n        <section aria-labelledby={`${id}-now`}>\n          <h2 id={`${id}-now`} className=\"mb-4 text-lg font-semibold tracking-tight\">\n            Happening now\n          </h2>\n          <ul className=\"grid gap-4\">\n            {active.map((inc) => (\n              <li key={inc.id} className=\"bg-card rounded-2xl border p-5 sm:p-6\">\n                <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1.5\">\n                  <h3 className=\"font-semibold tracking-tight\">{inc.title}</h3>\n                  <span className=\"bg-muted rounded-full border px-2.5 py-0.5 text-xs font-medium\">{impactLabel[inc.impact]}</span>\n                  {inc.affects?.length ? <span className=\"text-muted-foreground text-sm\">Affects {inc.affects.join(\", \")}</span> : null}\n                </div>\n                <ol className=\"mt-5 grid gap-5\">\n                  {inc.updates.map((u, i) => (\n                    <li key={i} className=\"relative ps-7\">\n                      <span aria-hidden=\"true\" className={cn(\"absolute start-0 top-1.5 size-3 rounded-full border-2\", i === 0 ? \"bg-foreground border-foreground\" : \"bg-background border-border\")} />\n                      {i < inc.updates.length - 1 ? <span aria-hidden=\"true\" className=\"bg-border absolute start-[5px] top-5 -bottom-6 w-px\" /> : null}\n                      <p className=\"text-sm\">\n                        <span className=\"font-semibold\">{stepLabel[u.status]}</span> <span className=\"text-muted-foreground tabular-nums\">· {u.time}</span>\n                      </p>\n                      <p className=\"text-muted-foreground mt-1 text-sm text-pretty\">{u.body}</p>\n                    </li>\n                  ))}\n                </ol>\n              </li>\n            ))}\n          </ul>\n        </section>\n      ) : null}\n\n      <section aria-labelledby={`${id}-svc`}>\n        <h2 id={`${id}-svc`} className=\"mb-4 text-lg font-semibold tracking-tight\">\n          Services\n        </h2>\n        <ul className=\"bg-card divide-y rounded-2xl border\">\n          {list.map((c) => {\n            const M = levelMeta[c.status]\n            return (\n              <li key={c.id} className=\"p-5 sm:p-6\">\n                <div className=\"mb-4 flex items-start justify-between gap-4\">\n                  <div className=\"min-w-0\">\n                    <h3 className=\"font-medium\">{c.name}</h3>\n                    {c.description ? <p className=\"text-muted-foreground mt-0.5 text-sm\">{c.description}</p> : null}\n                  </div>\n                  <p className=\"flex shrink-0 items-center gap-1.5 text-sm font-medium\">\n                    <M.icon aria-hidden=\"true\" className={cn(\"size-4\", M.tone)} />\n                    {M.label}\n                  </p>\n                </div>\n                <UptimeBar item={c} today={today} locale={locale} />\n              </li>\n            )\n          })}\n        </ul>\n      </section>\n\n      <section aria-labelledby={`${id}-past`}>\n        <h2 id={`${id}-past`} className=\"mb-4 text-lg font-semibold tracking-tight\">\n          Past incidents\n        </h2>\n        <ol className=\"grid gap-6\">\n          {rows.map((r) => (\n            <li key={r.days[0]} className=\"grid gap-3 sm:grid-cols-[9rem_minmax(0,1fr)]\">\n              <h3 className=\"text-muted-foreground text-sm font-medium tabular-nums\">{r.days.length > 1 ? `${dayFmt.format(toT(r.days.at(-1)!))} to ${dayFmt.format(toT(r.days[0]!))}` : dayFmt.format(toT(r.days[0]!))}</h3>\n              {r.here.length ? (\n                <ul className=\"grid gap-3\">\n                  {r.here.map((inc) => (\n                    <li key={inc.id} className=\"bg-card rounded-xl border p-4\">\n                      <p className=\"font-medium\">{inc.title}</p>\n                      <p className=\"text-muted-foreground mt-1 text-sm text-pretty\">{inc.updates[0]?.body}</p>\n                      <p className=\"text-muted-foreground mt-2 text-xs\">\n                        {impactLabel[inc.impact]}\n                        {inc.affects?.length ? ` · ${inc.affects.join(\", \")}` : \"\"} · Resolved {inc.updates[0]?.time}\n                      </p>\n                    </li>\n                  ))}\n                </ul>\n              ) : (\n                <p className=\"text-muted-foreground text-sm\">No incidents reported.</p>\n              )}\n            </li>\n          ))}\n        </ol>\n      </section>\n\n      <section aria-labelledby={`${id}-sub`} className=\"bg-muted/40 rounded-2xl border p-5 sm:p-6\">\n        <h2 id={`${id}-sub`} className=\"flex items-center gap-2 text-lg font-semibold tracking-tight\">\n          <Mail aria-hidden=\"true\" className=\"size-5\" />\n          Get told when something changes\n        </h2>\n        {sub === \"done\" ? (\n          <p role=\"status\" className=\"mt-3 text-sm\">\n            Subscribed. We will email {email.trim()} when an incident starts or ends.\n          </p>\n        ) : (\n          <form onSubmit={subscribe} noValidate className=\"mt-3 flex flex-col gap-2 sm:flex-row\">\n            <div className=\"flex-1\">\n              <Input type=\"email\" value={email} onChange={(e) => setEmail(e.target.value)} aria-label=\"Email address\" placeholder=\"name@company.com\" autoComplete=\"email\" aria-invalid={!!subError} aria-describedby={subError ? `${id}-err` : undefined} />\n              {subError ? (\n                <p id={`${id}-err`} role=\"alert\" className=\"text-destructive mt-1.5 text-sm\">\n                  {subError}\n                </p>\n              ) : null}\n            </div>\n            <Button type=\"submit\" disabled={sub === \"sending\"} className=\"sm:self-start\">\n              {sub === \"sending\" ? <Loader2 aria-hidden=\"true\" className=\"animate-spin motion-reduce:animate-none\" /> : null}\n              {sub === \"sending\" ? \"Subscribing\" : \"Subscribe\"}\n            </Button>\n          </form>\n        )}\n      </section>\n    </div>\n  )\n\n  return (\n    <section data-slot=\"status-1\" data-state={state} aria-labelledby={`${id}-title`} className={cn(\"mx-auto w-full max-w-3xl px-4 py-12 sm:px-6 sm:py-16\", className)} {...frameProps(props)}>\n      <h1 id={`${id}-title`} className=\"mb-8 text-3xl font-semibold tracking-[-0.03em] text-balance\">\n        {title}\n      </h1>\n      <StateView state={state === \"loading\" || state === \"error\" ? state : \"ready\"} onRetry={onRetry} label=\"Loading system status\" loading={<Skel />} error={{ title: errorState?.title ?? \"Status could not be loaded\", description: errorState?.description ?? \"This does not mean something is down. Check your connection and try again.\", ...errorState }}>\n        {content}\n      </StateView>\n    </section>\n  )\n}\n\nexport { Status1, type Status1Props, type Status1Component, type Status1Incident, type Status1Update, type Status1Level, type Status1Day }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/status-1/status-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "status",
      "uptime",
      "incidents",
      "system status",
      "outage",
      "maintenance",
      "reliability",
      "operational",
      "status page"
    ],
    "version": "1.0.0",
    "updated": "2026-10-18",
    "tests": true,
    "ai": {
      "summary": "A status page. <Status1 components={[{ id, name, description, status: 'operational' | 'degraded' | 'outage' | 'maintenance', days: ('ok' | 'degraded' | 'outage' | 'none')[] /* 90, oldest first */ }]} incidents={[{ id, title, impact: 'minor' | 'major' | 'maintenance', day: '2026-10-18', affects: ['API'], updates: [{ status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'scheduled', time: '09:42', body }] /* newest first */ }]} today='2026-10-18' updatedAt='10:42 UTC' onSubscribe />. The banner is the worst current status; an incident is live until its newest update is resolved. Pass today yourself; the block never reads the clock.",
      "whenToUse": [
        "A public status page for a service",
        "An internal reliability page showing uptime and incidents"
      ],
      "whenNotToUse": [
        "A single status dot or badge row (use status-dot or status-badge-row)",
        "Raw server logs (use log-stream)"
      ],
      "composesWith": [
        "header-1",
        "footer-1",
        "settings-5"
      ],
      "a11y": [
        {
          "keys": "Tab, then Left / Right / Home / End",
          "action": "Each history bar is a slider through its days; the value text reads the date and what happened, and a tooltip shows the same for pointer users"
        },
        {
          "keys": "Summary",
          "action": "Every history also has a sentence in text: the uptime and how many days had an outage or were degraded"
        },
        {
          "keys": "Status",
          "action": "Shown with an icon and a word (Operational, Degraded, Outage, Maintenance), never by color alone; the banner is a live region"
        }
      ],
      "customization": [
        "Uptime counts an outage day as down and a degraded day as half",
        "The bar and its day order run left to right in every language, like a chart",
        "Dates use the locale you pass"
      ]
    },
    "states": [
      {
        "id": "loading",
        "title": "Loading",
        "example": "status-1-loading",
        "trigger": "state=\"loading\"",
        "note": "A skeleton banner and service rows under the real heading, announced as loading."
      },
      {
        "id": "empty",
        "title": "No services listed",
        "example": "status-1-empty",
        "trigger": "state=\"empty\" (or components={[]})",
        "note": "Says services appear once added; emptyState sets the copy and can add a button."
      },
      {
        "id": "error",
        "title": "Could not load",
        "example": "status-1-error",
        "trigger": "state=\"error\"",
        "note": "An alert that says this does not mean something is down, with Try again (onRetry)."
      }
    ],
    "examples": [
      "status-1-demo",
      "status-1-operational",
      "status-1-outage",
      "status-1-loading",
      "status-1-empty",
      "status-1-error"
    ],
    "url": "https://ui.ballmac.com/components/status-1"
  },
  "categories": [
    "blocks",
    "status"
  ],
  "type": "registry:block"
}