AI insight card
ProA KPI card that explains itself. Explain streams a short reason for the change and ranks three drivers with contribution bars. A numbered annotation lights up on the sparkline as each driver is read, and follow-up chips ask the next question in place.
- A dashboard KPI where readers ask why it moved, such as net revenue, MRR, signups or churn.
- Weekly business reviews and finance dashboards that want an explanation next to the number.
- Products adding AI analysis to existing analytics without a separate chat.
- Open ended questions across many tables: use ask-your-data.
- A plain number with no explanation: use metric-card or stat-card.
- Dense multi series charts: use a full chart component.
Installation
Pro source and install commands unlock with a Pro plan.
Usage
Pass your KPI as `metric`: totals, the current and previous series, and point labels. Pass `followUps` for the chips. Wire `explain({ metric, followUp, signal })` to your analysis endpoint. It should resolve with `{ segments, drivers }`, where each driver has a signed delta and the series index its annotation marks. Write one text segment per driver, with `driver` set and in rank order, so the chart lights up as the reader reaches it. The signal aborts on Stop, Hide or a new question. If the promise rejects, the card shows an error row with Retry. Use `lowerIsBetter` for churn or cost metrics so a rise reads red, and `format` for metrics that are not currency. With `explain` set, autoPlay is off by default, so the card never calls the model on its own.
import { AiInsightCard } from "@/components/arc/blocks/ai-insight-card/ai-insight-card"; export function RevenueCard({ metric }) { return ( <AiInsightCard metric={metric} followUps={[{ id: "plan", label: "Break down by plan" }]} explain={async ({ metric, followUp, signal }) => { const res = await fetch("/api/insights/explain", { method: "POST", body: JSON.stringify({ metric: metric.label, followUp: followUp?.id }), signal, }); if (!res.ok) throw new Error("The analysis service did not respond."); return res.json(); // { segments: [{ text, driver? }], drivers: [{ id, label, delta, point }] } }} /> );}AiInsightCard
A KPI card with a sparkline that explains its own change: Explain streams a short reason, ranks three drivers with contribution bars and lights up a numbered chart annotation as each driver is read. Follow-up chips ask the next question in place.
metricInsightMetricsampleMetricThe KPI: label, period, value, previous, comparisonLabel, series, previousSeries, points (tooltip labels), legend labels, an optional format function and lowerIsBetter to flip delta colors.insightInsightsampleInsightThe local answer for Explain: segments of text (a segment with `driver` lights up that driver) and up to three ranked drivers, each with a signed delta and the series index its annotation marks.followUpsInsightFollowUp[]sampleFollowUpsFollow-up chips under the answer. Each has an id, a label and an optional local insight. The chip showing its answer reads as pressed.explain(request: { metric, followUp?, signal }) => Promise<Insight>–Fetch a real answer. Resolve with the full insight; the card streams it in chunks and syncs the annotations. The signal aborts on Stop, Hide or a new question. A rejection shows the error row with Retry.onExplain(followUp?: InsightFollowUp) => void–Called when Explain is pressed (no argument) or a follow-up is picked.autoPlaybooleantrue without explainPlays Explain and each follow-up on a loop while the card is on screen, until the reader interacts. Off with reduced motion.thinkingLabelstring"Reading invoices and renewals"Shimmering status line while the answer is prepared.source{ label: string; icon?: ReactNode } | nullStripe billingData source in the footer. Pass null to hide it.model{ label: string; icon?: ReactNode } | nullClaude Sonnet 5.5Model in the footer. Pass null to hide it.classNamestring–Class on the card root.- Tab
- Moves through Explain (Stop while streaming, Hide when open), the chart, Retry and the follow-up chips.
- EnterorSpace
- Explains the change, stops a stream, hides the panel or asks a follow-up.
- LeftorRight
- With the chart focused, steps through the points and announces each value.
- HomeorEnd
- Jumps to the first or last point.
- Escape
- Clears the chart readout.
- The card is a labelled section; Explain carries aria-expanded and aria-controls for the answer panel.
- A polite live region announces when the answer starts, the full answer when it is ready, Stop and errors. Without `explain` it says the answer is simulated from sample data.
- Deltas carry a sign as well as color, and unread driver rows are hidden from assistive tech until the answer reaches them.
- The chart is a focusable group with arrow key reading; values are announced through its own live region.
- Follow-up chips use aria-pressed for the answer on screen and are disabled while a stream runs.
- The panel opens and closes on a critically damped 260ms spring; answers of a different length spring to their height. No bounce.
- The answer streams 3 to 7 words every 34ms, each chunk only fading in over 140ms. Its full text is reserved underneath, so the card never grows while streaming.
- Annotations fade and scale in over 200ms as their driver is read; the driver being read takes the accent on its badge, dot, guide, rank and underline.
- Contribution bars grow to their share over 240ms ease-out; skeletons crossfade to labels in 160ms.
- Reduced motion: the answer appears whole after the short thinking state, transitions cut, and the demo loop stays off.
- The card fills its container up to 640px. Under 460px the legend hides and the contribution bars shorten, so driver labels keep their room and truncate with an ellipsis.
- Follow-up chips wrap onto a second row on phones. The chart redraws at the container width with crisp strokes.
- Touch scrubs the chart only while the finger is down, so vertical page scrolling still works.
- The chart is one inline SVG with two paths; annotations are absolutely positioned spans with CSS transitions.
- Streaming updates one counter per tick; tokenizing runs once per answer.
- The demo loop waits while the card is off screen and stops for good on the first interaction.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use for one KPI that needs a why: revenue, signups, churn, pipeline. Return drivers that add up to the change, ranked by absolute size, at most three.
- Point each driver at the series index where it shows, and write one text segment per driver with `driver` set, in rank order, so the chart lights up as the reader reaches it.
- Pass lowerIsBetter for costs and churn so a rise reads red. Pass `format` for non-currency metrics.
- With a real `explain`, autoPlay defaults to off so the card never spends model calls on its own.
The full library index for assistants is at /llms.txt.