# Design a Health Dashboard That Exposes Uncertainty and Connection Errors

> Source: <https://dev.to/babycat/design-a-health-dashboard-that-exposes-uncertainty-and-connection-errors-1412>
> Published: 2026-07-25 11:03:37+00:00

A lab card says “No data,” but that phrase could mean no matching record, a disconnected source, a delayed refresh, a filter mismatch, or a request failure. Collapsing those states into an empty chart makes uncertainty look like certainty. The interface needs two layers: what the source reports and what the application currently knows about that report.

OpenAI’s July 23, 2026 announcement says Health in ChatGPT is rolling out on web and iOS to eligible logged-in US users age 18 and older. According to [the announcement](https://openai.com/index/health-in-chatgpt/), supported connections include medical records and Apple Health, while the dashboard can cover labs, medications, activity, sleep, and other health information. OpenAI states connected data and relevant conversations are not used to train foundation models or target ads. This article treats those as attributed statements and proposes a generic UI pattern, not a reconstruction of that interface.

Do not make color carry the entire message. A card can use this **unexecuted TypeScript model**:

```
type SourceState =
  | { kind: "current"; observedAt: string; sourceLabel: string }
  | { kind: "stale"; lastSuccessAt: string; sourceLabel: string }
  | { kind: "disconnected"; sourceLabel: string }
  | { kind: "partial"; available: number; expected?: number }
  | { kind: "error"; retryable: boolean; reference: string };

type MeaningState =
  | { kind: "reported"; display: string; unit?: string }
  | { kind: "missing"; reason: "none_found" | "filtered" | "unknown" }
  | { kind: "unsupported"; explanation: string };

type HealthCard = { title: string; source: SourceState; meaning: MeaningState };
```

`SourceState`

answers whether retrieval is trustworthy enough to display. `MeaningState`

answers what can honestly be said about the value. Keeping them separate prevents a successful network response from implying interpretation and prevents an error from being shown as a zero.

```
<section aria-labelledby="sleep-title" aria-describedby="sleep-status">
  <h2 id="sleep-title">Sleep</h2>
  <p id="sleep-status">Last updated July 22 at 8:10 AM from connected source.</p>
  <p><strong>395 minutes</strong> <span>reported duration</span></p>
  <details>
    <summary>What this card can and cannot tell you</summary>
    <p>This shows received data. It does not interpret its medical meaning.</p>
  </details>
  <button type="button">Refresh</button>
</section>
<div role="status" aria-live="polite" aria-atomic="true"></div>
```

Use the live region only for the result of a user-triggered refresh, not every background synchronization. Keep focus on the refresh button after success. On a nonretryable connection failure, move neither focus nor the user into a dead end: expose “Review connection” beside a plain-language explanation.

| State | Heading text | Action | Announcement |
|---|---|---|---|
| stale | “Last updated…” | Refresh | only after requested refresh |
| disconnected | “Source disconnected” | Review connection | announce transition once |
| partial | “Some records unavailable” | Show included range | do not announce counts repeatedly |
| error | “Could not update” | Retry when safe | include retry result |
| missing | “No matching record found” | Review filters | distinguish from zero |

A skeleton loader should reserve layout but must not expose fake values to assistive technology. Disable neither navigation nor disconnect controls while one card refreshes. If several cards fail together, provide a page-level summary with links to affected headings rather than repeating identical alerts.

This is a proposed matrix, not executed results:

| Transition | Keyboard check | Screen-reader check | Visual check |
|---|---|---|---|
| current to stale | action remains reachable | freshness text is associated | icon plus text changes |
| refresh to error | focus stays stable | one concise status message | error is near card |
| connected to disconnected | disconnect remains reversible | source name is announced | no value shown as current |
| partial to current | details state persists | heading structure unchanged | chart does not jump |

Record browser, OS, zoom, contrast mode, and assistive-technology versions when running it. Test at 200% zoom and with CSS disabled to confirm source, freshness, value, and action remain in a sensible reading order.

This pattern does not establish data accuracy, clinical significance, accessibility conformance, product availability, or how any specific service models errors. It does not define whether a value is healthy and should never style a medical conclusion from an AI-generated inference. OpenAI says Health in ChatGPT is designed to support rather than replace medical care, and not for diagnosis or treatment. The interface should preserve that boundary at the value, not bury it in a footer.

AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited primary source.
