Skip to main content

StatCard

Implemented

KPI metric card — 40×40 colored icon + trend pill + 28px/800 value + 12px muted label. White card with 1px border, hover lift -2px + card shadow.

Categorymolecules
Sourcepatterns/molecules/statcard.html
AI HintUse for grouped UI patterns. Combine atoms into functional units.

A labelled metric card with optional trend arrow, icon, and unit suffix. Renders as a <div role="article"> or as an <a> wrapper when href is provided.

StatCard

Charts / Stats — Family 5 (Tier 1)

A self-contained KPI tile. Owns its label, value, unit, optional trend (up / down / flat with a directional arrow), and optional leading icon. Forwarded ref lands on the outer wrapper, and the component exposes aria-labelledby / aria-busy for assistive tech and loading-state hooks.

Tickets sold
12,480tickets
+12.4%vs last week
Gross revenue
$84,210
+$6,120vs last week
Refund rate
2.1%
-0.3 ppvs last month
Avg. order value
68.4
unchanged

This component supports light and dark mode.

Usage

StatCard is a small, opinionated KPI tile. Pass a label for the caption and a value for the headline number. Numbers go through toLocaleString(); pass a pre-formatted string when you need a currency symbol or a unit that does not belong to unit.

import { StatCard } from '@epasslive/design-system'

<StatCard
  label="Tickets sold"
  value={12480}
  unit="tickets"
  trend={{ direction: 'up', value: '+12.4%', label: 'vs last week' }}
/>

<StatCard
  label="Avg. order value"
  value={68.4}
  trend={{ direction: 'flat', value: 'unchanged' }}
/>

Trends

The trend prop is an object with a direction ("up" | "down" | "flat") and a value string that is rendered verbatim (so format it the way you want it to read). Direction drives both the arrow glyph and the color — emerald for up, rose for down, muted for flat. An optional label is appended in muted text and included in the aria-label ("Trend increased by +12.4% vs last week").

Loading state

Set loading to swap the body for a three-line shimmer skeleton and expose aria-busy="true" on the wrapper. The skeleton cycles through three opacity phases every 600 ms; the current phase is also exposed as data-skeleton-phase on the skeleton root for end-to-end testing.

Clickable card

Pass href to render the body inside an <a>. The wrapper stays role="article" so the announcement does not change, and the link picks up a teal focus ring (ep-teal) with a 2px offset. Use it for dashboard tiles that drill into a detail page.

Dark mode

The card uses bg-ep-surface-card and border-ep-border, which flip automatically with the active theme. Trend colors stay semantic (emerald = good, rose = bad) on both themes — the icons themselves are inline SVGs using currentColor, so the contrast tracks the surrounding text.

Props

Prop
Type
Default
Description
label*
string
—

Caption above the value. Wired to the wrapper via aria-labelledby.

value*
string | number
—

Primary metric. Numbers are formatted via toLocaleString(); pass a pre-formatted string for non-numeric metrics.

unit
string
—

Suffix appended to the value with muted styling (e.g. "USD", "%", "tickets").

trend
StatCardTrend
—

Trend indicator. { direction: "up" | "down" | "flat", value: string, label?: string }. Direction drives the arrow glyph and the color (emerald / rose / muted).

icon
React.ReactNode
—

Leading icon rendered to the right of the label. Marked aria-hidden so it does not duplicate the label for screen readers.

loading
boolean
false

When true, replaces the body with a three-line shimmer skeleton and sets aria-busy on the wrapper.

className
string
—

Additional classes appended to the inner card container (or the outer wrapper when href is set).

id
string
—

Explicit id. Defaults to "statcard-<react.useId()>". When set, the component generates per-part ids (label/value/trend) by appending "-label", "-value", and "-trend".

href
string
—

When provided, the body is wrapped in an <a> and the card becomes focusable with a teal focus ring. The card keeps role="article" so it stays announceable.

Props

Preview

StatCard

Prop
Type
Required
Default
Description
label
string
Required
—
No description.
value
string
Required
—
No description.
icon
React.ReactNode
Required
—
No description.
unit
string
Optional
—

Synced from source — no interface JSDoc yet; document on `StatCard.tsx`.

trend
StatCardTrend
Optional
—

Synced from source — no interface JSDoc yet; document on `StatCard.tsx`.

loading
boolean
Optional
false

Shows the busy state.

href
string
Optional
—

Link target URL.

className
string
Optional
—

Additional classes on the card container.

id
string
Optional
'statcard-{useId()}'

Explicit id; derives -label/-value/-trend ids.

hint
string
Optional
—

Optional muted hint line under the value (e.g. 'Set the venue capacity' when a metric is unset). Localized by the consumer.

Quick use:<StatCard />

Accessibility

ARIA rolearticle

Type

1 rule

NASA Rule 5

label, value, icon all required

ARIA

1 rule

WCAG 1.3.1

Article element must be aria-labelledby pointing to the value id, so SR reads the value first

Usage

Import

tsxSnippet
import { StatCard } from "@epasslive/design-system"

Example 1

tsxSnippet
<Card title="Event" body="Description" />

Anti-patterns

— Do not hardcode data in molecules

— Avoid complex business logic in molecules

Design System v4.1.0 | ePassLive