Metric Card
- Needs review
- WCAG 2.2 evidence
- RTL
A headline number with its label and context: type picks
the layout, simple, trend, sparkline, progress, comparison or icon, and isLoading shows the Figma loading
placeholder. Presentational: no state and no "use client", so it renders in React Server Components. The card is a labelled group (label, value, delta, caption in reading order). The delta badge says whether the figure went up or
down and whether that is good or bad in an arrow and in hidden text, never by colour alone (WCAG 1.1.1, 1.4.1); the progress bar
is a named progressbar with the figure as its text (4.1.2); the loading state is aria-busy with hidden text (4.1.3). The
layout adapts to the card's own width through a container query, and long or translated text wraps (1.4.10, 1.4.12). Use at most four to six cards in a row; use a chart for analysis. Consumer duties: formatting numbers for the locale, the label
and caption wording, the text alternative of a sparkline, and announcing when loaded data replaces the placeholder.
import { MetricCard } from "@nexera-ui/react";- 13
- examples
- 15
- props
- 3
- live controls
- 1
- platform
- 8
- WCAG criteria
- 5
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { MetricCard } from "@nexera-ui/react";
<MetricCard />Examples 12
The same examples as Storybook, rendered live. Open Code to copy one.
Trend
Type=Trend: a delta badge after the value.
import { MetricCard } from "@nexera-ui/react";
<MetricCard label="Headcount" value="128" caption="vs last month" type="trend" delta="+4.2%" />;
With sparkline
Type=Sparkline: pass a Sparkline chart in the sparkline slot; it drops below the value when the card is narrow.
import { MetricCard } from "@nexera-ui/react";
function SparklineStandIn({ label }: { label: string }) {
return (
<svg
role="img"
aria-label={label}
viewBox="0 0 90 36"
className="h-9 w-22.5 text-brand-primary"
>
<polyline
points="0,30 12,26 24,28 36,20 48,22 60,14 72,12 90,4"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
<div className="w-100 max-w-full">
<MetricCard
label="Monthly payroll"
value="Rs 29.0M"
caption="vs September"
type="sparkline"
delta="+4.2%"
sparkline={<SparklineStandIn label="Monthly payroll over the last 12 months, rising" />}
/>
</div>;
Progress
Type=Progress: a named progress bar that reads the figure.
import { MetricCard } from "@nexera-ui/react";
<MetricCard
label="Profile completion"
value="86%"
caption="110 of 128 employees"
type="progress"
progress={86}
/>;
Comparison
Type=Comparison: a delta against the previous period; here a fall is good news (trend="down-good").
import { MetricCard } from "@nexera-ui/react";
<MetricCard
label="Time to hire"
value="23 days"
caption="Last quarter: 31 days"
type="comparison"
delta="-7.4%"
trend="down-good"
/>;
With icon
Type=Icon: an icon tile before the label.
import { MetricCard } from "@nexera-ui/react";
import { LuUsers } from "react-icons/lu";
<MetricCard
label="On leave today"
value="6"
caption="3 annual · 2 sick · 1 other"
type="icon"
icon={<LuUsers />}
/>;
With info
info puts a control or icon at the end of the header, for example a tooltip that explains the metric.
import { MetricCard } from "@nexera-ui/react";
import { LuInfo } from "react-icons/lu";
<MetricCard label="Open roles" value="12" caption="4 in final interviews" info={<LuInfo />} />;
Loading
State=Loading (isLoading): three pulsing bars, aria-busy and visually hidden text.
import { MetricCard } from "@nexera-ui/react";
<MetricCard label="Open roles" value="12" caption="4 in final interviews" isLoading />;
Trends
The delta badge's trend says whether the change is good or bad: the arrow shows the direction, colour and hidden text the meaning.
import { MetricCard } from "@nexera-ui/react";
export function Trends() {
return (
<div className="grid grid-cols-[repeat(auto-fill,minmax(14rem,1fr))] gap-4">
<MetricCard
type="trend"
label="Headcount"
value="128"
delta="+4.2%"
trend="up-good"
caption="Growth is good"
/>
<MetricCard
type="trend"
label="Attrition"
value="3.1%"
delta="+0.4%"
trend="up-bad"
caption="A rise is bad"
/>
<MetricCard
type="trend"
label="Time to hire"
value="23 days"
delta="-7.4%"
trend="down-good"
caption="A fall is good"
/>
<MetricCard
type="trend"
label="Revenue"
value="Rs 4.1M"
delta="-1.8%"
trend="down-bad"
caption="A fall is bad"
/>
<MetricCard
type="trend"
label="Satisfaction"
value="4.2"
delta="0%"
trend="flat"
caption="No change"
/>
</div>
);
}
Kpi row
The Figma Chart/KPI/* family (Headcount, Attrition, Open roles, Time to hire) folded into MetricCard type="trend" (owner decision D1): the same layout with different data.
import { MetricCard } from "@nexera-ui/react";
export function KpiRow() {
return (
<div className="grid grid-cols-[repeat(auto-fill,minmax(14rem,1fr))] gap-4">
<MetricCard
type="trend"
label="Headcount"
value="128"
delta="+4.2%"
caption="vs last month"
/>
<MetricCard
type="trend"
label="Attrition"
value="3.1%"
delta="+0.4%"
trend="up-bad"
caption="vs last month"
/>
<MetricCard type="trend" label="Open roles" value="12" delta="+2" caption="vs last month" />
<MetricCard
type="trend"
label="Time to hire"
value="23 days"
delta="-7.4%"
trend="down-good"
caption="vs last quarter"
/>
</div>
);
}
Long content
Long and translated text wraps; the figure wraps rather than truncating.
import { MetricCard } from "@nexera-ui/react";
<MetricCard
label="Durchschnittliche Zeit bis zur Einstellung ueber alle Abteilungen und Standorte"
value="1.234.567,89 Rs"
caption="Letztes Quartal: 31 Tage, einschliesslich aller Standorte und Remote-Stellen"
type="trend"
delta="+12,4 %"
/>;
Right to left
Right-to-left: the text aligns to the start, the delta keeps its arrow, numbers stay left-to-right.
import { MetricCard } from "@nexera-ui/react";
<MetricCard
label="عدد الموظفين"
value="128"
caption="مقارنة بالشهر الماضي"
type="trend"
delta="+4.2%"
/>;
Narrow container
A 14 rem container: the card keeps its padding and the value row wraps.
import { MetricCard } from "@nexera-ui/react";
function SparklineStandIn({ label }: { label: string }) {
return (
<svg
role="img"
aria-label={label}
viewBox="0 0 90 36"
className="h-9 w-22.5 text-brand-primary"
>
<polyline
points="0,30 12,26 24,28 36,20 48,22 60,14 72,12 90,4"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
<div className="max-w-56">
<MetricCard
label="Monthly payroll"
value="Rs 29.0M"
caption="vs September"
type="sparkline"
delta="+4.2%"
sparkline={<SparklineStandIn label="Monthly payroll over the last 12 months, rising" />}
/>
</div>;
Props 15
Press "Try it" on a card to load that prop into the playground.
15 props shown
typeNexera"icon" | "trend" | "sparkline" | "simple" | "progress" | "comparison"Layout of the card: simple (value and caption), trend (value, delta badge, caption), sparkline (value,
delta badge and a sparkline), progress (value and a bar), comparison (value, delta badge against the previous period)
and icon (an icon tile before the label). The Figma Chart/KPI/* components (Headcount, Attrition, Open roles, Time to
hire) are type="trend" cards: the same layout with different data.
Progress card.
progressNexeranumberOnly with type="progress".
How far along, 0 to 100. The bar is a progressbar named by the label and
announcing value ("86%") as its text.
progressLabelNexerastringOnly with type="progress".
Replaces the label as the accessible name of the bar, for example "Profile completion, employees with a complete profile".
Translate it.
label*NexeraReactNodeWhat is measured. Short noun phrase. It names the card for assistive technology (the card is a labelled group), and wraps instead of truncating.
value*NexerastringThe headline figure. Format numbers, currency and
dates for the reader's locale (Intl.NumberFormat) and include the unit.
captionNexeraReactNodeContext under the figure. Shown when provided.
iconNexeraReactNodeIcon in the tile before the label, shown with
type="icon". 18 px, decorative.
infoNexeraReactNodeContent at the end of the header. Pass an IconButton inside a
Tooltip that explains how the metric is calculated, or a plain decorative icon. Nothing is drawn by default: a bare icon
would look like a control that does nothing.
deltaNexerastringThe change since the comparison period, for example "+4.2%", shown after the
value with type trend, sparkline or comparison. Format it for the locale and include the sign.
trendNexera"up-good" | "up-bad" | "down-good" | "down-bad" | "flat"Direction and meaning of delta: an arrow for the direction and a green, red or grey
badge for good, bad or no change, plus hidden text, so neither relies on colour alone for assistive technology. Figma's
samples use up-good for trend and sparkline and down-good for comparison.
deltaLabelsNexeraPartial<Record<DeltaBadgeTrend, string>>Texts read after delta, per trend ("Increase, good"); see DeltaBadge. Translate all five for your locale.
sparklineNexeraReactNodeThe sparkline for type="sparkline": pass a Sparkline chart with its own text
alternative. It sits after the value on the same row from a 20 rem container, below it in a narrower card. Replaces nothing
when type is another value.
isLoadingNexerabooleanShows a placeholder instead of the content: three pulsing bars. The card is aria-busy and carries
visually hidden text (loadingLabel), so assistive technology knows the figure is not there yet (WCAG 4.1.3). Announce the
result when the data arrives if it matters.
loadingLabelNexerastringVisually hidden text of the loading state. Translate it.
classNameNexerastringExtra classes, merged last so they win over the defaults.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
5 direct · 3 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.1Use of ColorLevel A · tested directly
- 1.4.10ReflowLevel AA · supporting test
- 1.4.12Text SpacingLevel AA · supporting test
- 2.3.3Animation from InteractionsLevel AAA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
- 4.1.3Status MessagesLevel AA · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
<MetricCard className="shadow-sm" />Used in blocks
- Sidebar app shellApp shells and navigation
- KPI overviewDashboards and analytics
- Sales dashboardDashboards and analytics
- Chart statesDashboards and analytics
- Loading skeletonsStates and system messages
Related components
- AccordionItemOne collapsible section of an FAQ or settings page.
- AreaChartA stacked area chart: layers piled up over time with the title, range switch, actions and legend of the card.
- AvailabilityRowOne person's availability across a window of the day: an avatar with initials and the name, and a timeline with the busy periods drawn as bars.
- AvatarA person or organisation shown as a photo, initials or an icon.
- AvatarGroupA row of overlapping avatars with a "+N" chip for the rest.
- AvatarLabelAn avatar with a name and an optional subtitle beside it, for a person in a list, a header or a mention.
- BadgeShort status or category label: `Tone` x `Variant` x `Size`, with an optional dot, leading icon and remove button.
- BadgeGroupA pill that pairs a small badge with a short message, for an announcement or a status line.