Patterns

KPI card

A single-metric card for the Yuno Dashboard: label + big value (with optional suffix) + optional View more CTA + optional trend row (arrow + delta % + period text). The canonical shape any prototype uses to surface a KPI on the Home, Insights, Reports, or a section header.

Updated Sep 28, 2026 by Juan Pablo Turina

Anatomy

A Card split into two rows by a border. Top row: label + big value (with optional currency suffix) on the left, an optional View more Button on the right. Bottom row: a trend arrow (Phosphor light) + a colored delta % + a period text. Hover lifts the card (primary border + shadow-xl — same lift as ConnectionCard / RoutingCard).

Total payments

$284,512USD

+9% compared to yesterday

  1. 1
    Label

    Metric name at the top of the card (text-sm muted). Keep it short — 1 to 3 words.

  2. 2
    Value + suffix

    The big number, with the t-figure level. The optional suffix (e.g. USD) renders next to it at a lighter weight.

  3. 3
    View more CTA

    Outline Button (sm), top-right. Optional. Omit onViewMore and the button is not rendered.

  4. 4
    Trend row

    Bottom row, below a divider: TrendUp / TrendDown Phosphor icon + a colored delta % + the period text (default 'compared to yesterday'). The arrow follows the sign; the colour follows whether that is good news (goodWhen): text-success for good, text-destructive for bad, muted with no arrow for no change.

Variants

Good news

The metric moved the way that is good for it: text-success. With the default goodWhen="up", that is a rise (payments, volume, approval rate). Never fake a good trend to colour the row green.

Total payments

$284,512USD

+9% compared to yesterday

<KpiCard label="Total payments" value="$284,512" valueSuffix="USD" trend={9} onViewMore={() => {}} />
Bad news

The metric moved the wrong way: text-destructive, a semantic signal, not a decoration. This is one of the few places the destructive token is legitimate in a Yuno dashboard.

Conversion rate

82.4%

-3% compared to yesterday

<KpiCard label="Conversion rate" value="82.4%" trend={-3} />
When a fall is good news

goodWhen="down" for a decline rate, chargebacks, latency or a cost. The arrow still follows the sign (the number fell), and the colour follows the meaning (that is good). Without it, a chargeback rate that rose drew a green arrow up.

Chargeback rate

0.4%

+0.1% vs last month

Chargeback rate

0.3%

-0.1% vs last month

<KpiCard label="Chargeback rate" value="0.4%" trend={0.1} goodWhen="down" trendPeriod="vs last month" />
<KpiCard label="Chargeback rate" value="0.3%" trend={-0.1} goodWhen="down" trendPeriod="vs last month" />
No change

A trend of 0 is neither good nor bad news: muted text, no arrow, no sign.

Approved payments

12,847

0% compared to yesterday

<KpiCard label="Approved payments" value="12,847" trend={0} />
No trend row

Omit the trend prop and the whole bottom row disappears. Use for metrics that don't have a meaningful period comparison (e.g. account count) or when you want a leaner KPI block.

Approved payments

12,847

<KpiCard label="Approved payments" value="12,847" />

Recipes

Stacked (Home performance)

Three KpiCards stacked vertically next to a chart. The canonical Home layout: chart on the left in a 2-column grid, three KPIs stacked on the right with a fixed width. Each KpiCard has a View more CTA that opens the metric's detail view.

Total payments

$284,512USD

+9% compared to yesterday

Approved payments

12,847

+6% compared to yesterday

Conversion rate

94.2%

+4% compared to yesterday

<div className="flex flex-col gap-5">
  <KpiCard label="Total payments" value="$284,512" valueSuffix="USD" trend={9} onViewMore={() => {}} />
  <KpiCard label="Approved payments" value="12,847" trend={6} onViewMore={() => {}} />
  <KpiCard label="Conversion rate" value="94.2%" trend={4} onViewMore={() => {}} />
</div>
3-column grid

Three KpiCards side by side in a responsive grid. Use on a report or an insights section where the KPIs are peers of equal weight and no chart shares the row.

Total payments

$284,512USD

+9% compared to yesterday

Approved payments

12,847

+6% compared to yesterday

Conversion rate

94.2%

+4% compared to yesterday

<div className="grid grid-cols-1 gap-5 md:grid-cols-3">
  <KpiCard label="Total payments" value="$284,512" valueSuffix="USD" trend={9} />
  <KpiCard label="Approved payments" value="12,847" trend={6} />
  <KpiCard label="Conversion rate" value="94.2%" trend={4} />
</div>

Import

Copy this import line at the top of the file where you compose this molecule.

A molecule composed of Card + Button + Phosphor trend icons. Drop it in with props — three required strings and everything else optional.
import { KpiCard } from "@/components/patterns/kpi-card";

Props

Everything else is forwarded to the underlying elements via ...props.

PropTypeDefaultDescription
labelstring (required)—Metric label shown at the top of the card.
valuestring (required)—Formatted metric value. Big number. E.g. "$284,512", "12,847", "94.2%".
valueSuffixstring—Optional suffix rendered next to the value at a lighter weight. E.g. "USD".
trendnumber—Percentage delta. The arrow follows its sign; the colour follows goodWhen. 0 is muted with no arrow. Omit to hide the whole trend row.
goodWhen"up" | "down""up"Which way is good news. "down" for a decline rate, chargebacks, latency or a cost: a fall turns text-success, a rise text-destructive.
trendPeriodstring'compared to yesterday'Text after the trend %, describing the comparison window.
onViewMore() => void—View more CTA handler. When omitted the button is not rendered.
viewMoreLabelstring'View more'View more CTA label.
classNamestring—Extra classes merged onto the Card.

Related

Cross-links to the atoms this molecule composes and sibling patterns.

  • CardThe atom this molecule is built on.
  • ButtonThe View more CTA (variant outline, size sm).
  • Colorstext-success and text-destructive semantics on the trend row.