Account health
ProA customer success card where a health score gauge, a renewal countdown and five sourced signals with sparklines lead into an AI risk summary and one recommended next step, like scheduling a QBR before usage slips further.
- A customer success or account page that needs a score, the signals behind it and a next step.
- Renewal prep views where the countdown and risk drivers matter more than raw metrics.
- Showing an AI written risk summary next to the evidence it was written from.
- Portfolio views across many accounts; use a table with a score column instead.
- Deep metric exploration with axes and ranges; use a full chart.
- Single KPI tiles with no recommendation; use a stat card.
Installation
Pro source and install commands unlock with a Pro plan.
Usage
Drop it into an account or renewal page and pass `account` (name, plan, ARR, logo, term start and renewal date) and `periods` (one per time range, each with a score, the previous score, signals, a summary and an action). Pass `summarize(input, signal)` to return the summary text from your model. The card handles the thinking, streaming, abort and error states. Pass `onAction(action, input, signal)` to book the QBR for real, and reject it to show the retry state. `period`/`onPeriodChange` control the time range, and `asOf` fixes the countdown date. Keep the same number of trend points in every period so the sparklines morph from one to the next. With no props it plays a sample Linear Enterprise account.
import { AccountHealth } from "@/components/arc/blocks/account-health/account-health"; export function CustomerPanel({ account, periods }) { return ( <AccountHealth account={account} periods={periods} summarize={async ({ account, period }, signal) => { const res = await fetch("/api/health-summary", { method: "POST", body: JSON.stringify({ id: account.name, period: period.id }), signal }); return (await res.json()).text; }} onAction={async (action, { account }, signal) => { await fetch("/api/qbr", { method: "POST", body: JSON.stringify({ account: account.name }), signal }); }} /> );}API reference
3 parts. The first is the root.
AccountHealth
A customer success card: customer and plan, a health score gauge with trend, a renewal countdown, signal rows with sparklines and status, and an AI risk summary that ends in one recommended next step. With no props it plays a sample Linear Enterprise account.
accountAccountHealthAccountLinear sampleThe customer: name, plan, arr (formatted string), logo (ReactNode, about 24px), termStart and renewalDate (Date or ISO date).periodsAccountHealthPeriod[]30d and 90d samplesOne entry per time range: id, label, spanLabel, score, previousScore, signals, summary and action. Keep the same number of trend points across periods so sparklines morph.periodstring–Controls the selected period id. Pair with onPeriodChange.defaultPeriodstringfirst periodThe period selected on mount when uncontrolled.onPeriodChange(period: string) => void–Called when someone picks another time range.summarize(input: { account, period }, signal: AbortSignal) => Promise<string>–Writes the risk summary. Return the full text and the card streams it in. Aborted when the period changes or the summary is regenerated. Leave it out to use each period's summary, simulated.onAction(action, input: { account, period }, signal: AbortSignal) => Promise<void> | void–Runs the recommended action, such as booking a QBR. Reject to show the error state with a retry. Leave it out to simulate it.model{ name: string; mark?: ReactNode }{ name: "Claude Sonnet 5.5" }The model credited on the summary, with its 14px mark. Defaults to the Claude mark.asOfDate | stringtodayThe date the renewal countdown counts from. The sample account uses Oct 27, 2026.classNamestring–Extra class on the card.AccountHealthSignal
One signal row.
idRequiredstring–Stable key, shared across periods.labelRequiredstring–"Product usage", "Seat utilization", "Support tickets", "NPS", "Invoice status".detailstring–One short line of evidence: "Analytics down 18%".valueRequiredstring–The headline value, right aligned in tabular numerals.statusRequired"good" | "watch" | "risk"–Shown as an icon plus Good, Watch or At risk, and colors the sparkline when not good.trendnumber[]–Sparkline points, oldest first.sourceReactNode–The source's 14px mark, such as PostHog, Okta, Zendesk, Intercom or Stripe.sourceLabelstring–Names the source for screen readers: "From Zendesk".AccountHealthAction
The recommended next step under the summary.
labelRequiredstring–Button label: "Schedule QBR".titleRequiredstring–The recommendation as a sentence: "Schedule a QBR with Linear".reasonRequiredstring–Why, in a few words: "Usage dropped 18% in Analytics".doneLabelstring–Shown once the action has run: "Invite drafted for Tue, Nov 3, 10:00".- Tab
- Moves through the time range, regenerate and action buttons.
- ArrowLeftorArrowRight
- Switches the time range inside the segmented control.
- EnterorSpace
- Regenerates the summary or runs the recommended action.
- The card is a labelled section; the gauge is one image with the score and band in its label.
- Every status pairs an icon with a word (Good, Watch, At risk, Needs attention), never color alone.
- Signal sources are named for screen readers; sparklines are decorative and hidden.
- A polite live region reports the action result and says when it was simulated ("Simulated, no invite was sent").
- The summary is aria-busy while it is written; the action row stays inert until it appears.
- On first view the gauge sweeps and the score counts up from one motion value (560ms ease-out), sparklines draw in with a 40ms stagger, and the term bar fills.
- The summary thinks for about 0.9s with a shimmer on the label, then streams 3 to 6 words every 34ms, each chunk fading in over 140ms.
- Switching the time range morphs each sparkline point to point and crossfades the trend line; no element changes size.
- Reduced motion jumps to end states, shows the summary at once and keeps all feedback.
- Holds 600px at most and fills narrower containers.
- Below 520px card width the sparklines step aside and padding drops to 16px; below 440px the score and renewal stack.
- Labels and details truncate with an ellipsis; values and status keep fixed columns.
- Pure SVG and CSS; one motion value drives the gauge and number.
- The entrance starts on first intersection and runs once; no loops besides the label shimmer while thinking.
- Summary runs are aborted on period change, regenerate and unmount.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Pick this for a customer success, account management or renewal view about one customer.
- Pass real signals per period; keep 5 signals or fewer so the card stays one glance.
- Return plain text from summarize; the card handles thinking, streaming, abort and error states.
- The action button uses the accent; keep destructive actions out of this slot.
The full library index for assistants is at /llms.txt.