DEV Community

Design a Health Dashboard That Exposes Uncertainty and Connection Errors

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, 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.

The Proposed Pattern

Give each card an evidence state. 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.

Semantic Card Structure

<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.

Content Matrix

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.

Keyboard and Assistive-Technology QA

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.

Boundaries

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.

Comments

No comments yet. Start the discussion.