Skip to content

Insight card

A generated insight with a metric, a small chart and its sources.

Status
Beta
Category
AI
Adoption
Not used yet
import { InsightCard } from "@oration/canon/components/ai/insight-card";
packages/canon/src/components/ai/insight-card.tsx

Texting the remittance link shortens calls

Based on 1,422 remittance calls this week

41 sshorter average callDown 18%
Calls where Nora offered to text the remittance advice ended 41 seconds sooner and needed fewer repeats of the ACH trace number. The payment status line doesn't offer the text yet.

Sources2

  1. 1Call on Sep 24Call with Northwind FreightCan you just text me that number?
  2. 2DocumentRemittance advice FAQSuppliers can get the remittance by email or text.
import { InsightCard } from "@oration/canon/components/ai/insight-card";import { Button } from "@oration/canon/components/button";import { MiniChart } from "@oration/canon/components/mini-chart";import { toast } from "@oration/canon/components/toast";export function Hero() {    return (        <InsightCard            className="w-full max-w-md text-left"            title="Texting the remittance link shortens calls"            meta="Based on 1,422 remittance calls this week"            metric={{                label: "shorter average call",                value: "41 s",                delta: -0.18,                goodDirection: "down",            }}            chart={                <MiniChart                    data={[                        212, 208, 204, 199, 187, 176, 171, 168, 166, 162, 159,                        158, 156, 154,                    ]}                    type="area"                    width={320}                    height={40}                    ariaLabel="Average remittance call length, 14-day trend"                    className="h-10 w-full"                />            }            summary="Calls where Nora offered to text the remittance advice ended 41 seconds sooner and needed fewer repeats of the ACH trace number. The payment status line doesn't offer the text yet."            sources={[                {                    id: "s1",                    title: "Call with Northwind Freight",                    snippet: "Can you just text me that number?",                    kind: "transcript",                    meta: "Call on Sep 24",                },                {                    id: "s2",                    title: "Remittance advice FAQ",                    snippet:                        "Suppliers can get the remittance by email or text.",                    kind: "doc",                },            ]}            actions={                <>                    <Button                        type="button"                        variant="outline"                        size="sm"                        onClick={() =>                            toast.add({ title: "Coaching drafted for Nora" })                        }                    >                        Create coaching                    </Button>                    <Button                        type="button"                        variant="ghost"                        size="sm"                        onClick={() =>                            toast.add({                                title: "Opened 1,422 remittance calls",                            })                        }                    >                        Compare calls                    </Button>                </>            }        />    );}

Usage#

Insight card reports one finding the model surfaced from Cedarline's data: a title that states the finding, what it's based on, an optional metric in a row with its change, an optional small chart, the explanation, its sources and what to do next. It fills the Agents home What changed this week, ticket report insights, campaign rails and Copilot answers. The metric is a 20px figure beside its label, not a hero number, so a row of insight cards never turns into a metric mosaic. The common mistake is titling it with a metric name instead of the finding.

When to use

  • For a weekly finding on a home or overview page: Texting the remittance link shortens calls.
  • Beside an analytics chart, to say in words what moved and why.
  • Inside a Copilot answer to an analysis question, when there's one metric worth naming.
  • When the finding needs its evidence: calls, records or documents it came from.

When not to use

  • For a next step with a confidence level. Use Recommendation card
  • For a row of headline metrics at the top of a page. Use Inline stat strip
  • For one metric with its label and change and no explanation. Use Stat strip
  • For a trend that deserves axes, a legend and a tooltip. Use Chart
  • For a generated summary of one record or conversation. Use Streaming text

No hero metrics

DESIGN.md refuses hero-metric card grids. The insight's metric is a 20px figure on the same line as its label and delta, and the card's job is the sentence, not the number.

The Tabular Figures Rule

The metric value and delta are set in tabular figures so cards side by side line up.

The Ink Fill Rule

Charts inside the card are ink, such as an area MiniChart in its default tone. Only the delta turns semantic, because better or worse is a verdict.

The Quiet Indigo Rule

The sparkles mark is Slate Meta. Nothing in the card is indigo.

Anatomy#

Transfers to AP rose on the disputes line

Based on 318 calls this week

22%transferredUp 5 pts
Most new transfers are disputes over $5,000, which always go to a person.

Sources1

  1. 1DocumentDispute routing policyDisputes over $5,000 transfer to AP.
  1. Mark. A 16px Slate Meta icon, sparkles by default. Decorative.
  2. Title. An h3 at 14px, weight 600: the finding as a sentence.
  3. Meta. Optional 12px Slate Meta: what it's based on.
  4. Metric. Optional: the value at 20px, weight 600, tabular; its label in 13px Slate Meta; then the delta.
  5. Delta. 12px medium tabular with an up-right or down-right arrow. Ledger Green when the direction is good, Signal Red when it isn't.
  6. Chart. Optional, full width. Usually a 40px area MiniChart.
  7. Summary. 13px ink at 85%: what happened and why.
  8. Sources. Optional numbered source cards in a sideways-scrolling row.
  9. Actions. Buttons in a wrapping row, usually outline.

Examples#

Deltas and good direction

A string delta shows as written; a number is a fraction shown as a percentage. goodDirection decides the color, so a rising transfer rate is red.

Spanish line containment rose after the numbers coaching

Since Sep 2, 894 calls

74%containmentUp 6 pts
Reading invoice IDs one character at a time cut repeat requests by half.

Halcyon calls are transferring more often

Based on 46 calls this month

31%transfer rateUp 12%
Most are about the early-payment discount, which Nora can't explain from the remittance alone.
import { InsightCard } from "@oration/canon/components/ai/insight-card";import { PhoneForwardedIcon } from "lucide-react";export function Deltas() {    return (        <div className="grid w-full max-w-3xl gap-4 text-left md:grid-cols-2">            <InsightCard                title="Spanish line containment rose after the numbers coaching"                meta="Since Sep 2, 894 calls"                metric={{                    label: "containment",                    value: "74%",                    delta: "+6 pts",                    goodDirection: "up",                }}                summary="Reading invoice IDs one character at a time cut repeat requests by half."            />            <InsightCard                icon={<PhoneForwardedIcon />}                title="Halcyon calls are transferring more often"                meta="Based on 46 calls this month"                metric={{                    label: "transfer rate",                    value: "31%",                    delta: 0.12,                    goodDirection: "down",                }}                summary="Most are about the early-payment discount, which Nora can't explain from the remittance alone."            />        </div>    );}

Without a metric

Some findings are a sentence and an action. Leave out the metric and the chart rather than inventing a number.

Three suppliers account for half of this week's payment calls

From 214 supplier calls since Monday

Northwind Freight, Orchard Street and Halcyon called 107 times between them. Each has an invoice in Tuesday's ACH run, and most asked when it will land.
import { InsightCard } from "@oration/canon/components/ai/insight-card";import { Button } from "@oration/canon/components/button";import { toast } from "@oration/canon/components/toast";export function TextOnly() {    return (        <InsightCard            className="w-full max-w-md text-left"            title="Three suppliers account for half of this week's payment calls"            meta="From 214 supplier calls since Monday"            summary="Northwind Freight, Orchard Street and Halcyon called 107 times between them. Each has an invoice in Tuesday's ACH run, and most asked when it will land."            actions={                <Button                    type="button"                    variant="outline"                    size="sm"                    onClick={() =>                        toast.add({                            title: "Remittance emails drafted for 3 suppliers",                        })                    }                >                    Email their remittances early                </Button>            }        />    );}

On a home page

Two up under a Title-sized heading, each with a 14-day ink area chart and one outline action.

What changed this week

Texting the remittance link shortens calls

Based on 1,422 remittance calls this week

41 sshorter average callDown 18%
Calls ended sooner when Nora offered to text the remittance advice.

Missing W-9s are holding more invoices

Friday's payment run, 212 invoices

17invoices on holdUp 9
Eleven of the holds are suppliers onboarded since September 1.
import { InsightCard } from "@oration/canon/components/ai/insight-card";import { Button } from "@oration/canon/components/button";import { MiniChart } from "@oration/canon/components/mini-chart";import { toast } from "@oration/canon/components/toast";export function HomeSection() {    const insights = [        {            id: "ins_sms",            title: "Texting the remittance link shortens calls",            meta: "Based on 1,422 remittance calls this week",            metric: {                label: "shorter average call",                value: "41 s",                delta: -0.18,                goodDirection: "down" as const,            },            trend: [                212, 208, 204, 199, 187, 176, 171, 168, 166, 162, 159, 158, 156,                154,            ],            summary:                "Calls ended sooner when Nora offered to text the remittance advice.",            action: "Create coaching",        },        {            id: "ins_w9",            title: "Missing W-9s are holding more invoices",            meta: "Friday's payment run, 212 invoices",            metric: {                label: "invoices on hold",                value: "17",                delta: "+9",                goodDirection: "down" as const,            },            trend: [6, 7, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],            summary:                "Eleven of the holds are suppliers onboarded since September 1.",            action: "Request W-9s",        },    ];    return (        <section            aria-labelledby="insights-demo-heading"            className="w-full max-w-3xl text-left"        >            <h2                id="insights-demo-heading"                className="mb-3 text-sm font-semibold"            >                What changed this week            </h2>            <div className="grid gap-4 md:grid-cols-2">                {insights.map((item) => (                    <InsightCard                        key={item.id}                        title={item.title}                        meta={item.meta}                        metric={item.metric}                        summary={item.summary}                        chart={                            <MiniChart                                data={item.trend}                                type="area"                                width={320}                                height={40}                                ariaLabel={`${item.title}, 14-day trend`}                                className="h-10 w-full"                            />                        }                        actions={                            <Button                                type="button"                                variant="outline"                                size="sm"                                onClick={() =>                                    toast.add({ title: item.action })                                }                            >                                {item.action}                            </Button>                        }                    />                ))}            </div>        </section>    );}

States#

States
StateTreatment
Good changeThe delta moves in goodDirection: green, with the arrow in that direction.
Bad changeThe delta moves against it: red.
No metricLeave out metric and the card is title, summary, sources and actions.
Loading, empty, errorNot built in. Pages wrap the cards in DataState with a SkeletonCard per card, an empty state that teaches, and a retry.

Behavior#

  • It's a static <article>; only actions and linked sources are interactive.
  • A numeric delta is a fraction: -0.18 renders 18% with a down arrow. A string is shown as written without its sign, such as +6 pts as 6 pts with an up arrow.
  • Direction comes from the sign. goodDirection (default up) decides the color only; the arrow always shows the direction.
  • chart renders full width between the metric and the summary. Size the chart to its container, such as className="h-10 w-full".
  • sources renders SourceCards in the row layout.

Do and don't#

Texting the remittance link shortens calls

41 sshorter average callDown 18%
Calls ended sooner when Nora offered the text.
Do. Title the card with the finding: Texting the remittance link shortens calls.

Average call duration

41 sshorter average callDown 18%
Calls ended sooner when Nora offered the text.
Don't. Title it with the metric: Average call duration. The reader has to work out what changed and whether it matters.

Spanish line containment rose

74%containmentUp 6 pts
Reading IDs one character at a time halved repeat requests.
Do. Keep the metric on one line with its label and change, and let the summary explain it.
Containment74%+6
Calls894+12%
Don't. Build a grid of big numbers with tiny labels and trend chips. It's the hero-metric mosaic the system refuses.

Content#

  • Title: the finding as a sentence in sentence case, with the cause if known: Spanish line containment rose after the numbers coaching.
  • Meta: the base and the window: Based on 1,422 remittance calls this week, Since Sep 2, 894 calls.
  • Metric label: lowercase noun phrase that reads after the value: 41 s shorter average call, 74% containment.
  • Summary: two sentences at most. What changed, why, and what isn't done yet.
  • Actions: verbs that lead somewhere: Create coaching, Compare calls.

Accessibility#

  • The card is an <article> with an h3, reachable by heading.
  • The delta adds a hidden Up or Down before its figure, so the direction is read out. Whether that's good isn't announced (see known gaps).
  • The mark is aria-hidden.
  • Give a MiniChart an ariaLabel that names the series and window, such as Average call length, 14-day trend.

Design tokens#

Design tokens
TokenUsed for
--cardCard surface
shadow-borderHairline lift
--muted-foregroundMark, meta, metric label
--foregroundTitle, metric value; summary at 85%
--successDelta in the good direction
--destructiveDelta in the bad direction
--radius-xl12px card corners

API reference#

InsightCard

One finding. Also exported: the InsightMetric type.

Other props spread onto Nothing. Only the props below are read..

Props of InsightCard
PropTypeDefaultDescription
titleRequiredstringNo defaultThe finding as a sentence.
summaryRequiredReact.ReactNodeNo defaultWhat happened and why.
metric{ label: string; value: React.ReactNode; delta?: number | string; goodDirection?: "up" | "down" }No defaultOne metric in a row. A numeric delta is a fraction; goodDirection defaults to up.
chartReact.ReactNodeNo defaultUsually a MiniChart.
sourcesSource[]No defaultRendered as a source card row. See Source cards.
actionsReact.ReactNodeNo defaultButtons.
metaReact.ReactNodeNo defaultWhat it's based on.
iconReact.ReactNodeNo defaultReplaces the sparkles mark.
classNamestringNo defaultMerged onto the article.

Known gaps#

Where the implementation and the system disagree today. Follow the system, not the gap.

Whether a delta is good or bad is shown by color only. The arrow and the hidden Up or Down give the direction, not the judgment.

A string delta loses its sign in the text (+6 pts shows as 6 pts); only the arrow keeps it.

The summary is ink at 85% (text-foreground/85), an alpha that isn't one of the named text tokens.

No loading, empty or error state. Every page wraps the cards in DataState itself.

The title is always an h3.