Hacktron Design System

MetricCard

A dashboard tile showing one number, its trend, and an optional sparkline.

Active repositories
4,820
43%
vs. previous 24 hours
<template>  <MetricCard    label="Active repositories"    :value="activeRepoCount.toLocaleString()"    :trend="repoTrend"  /></template>

Props

Name Type Default
labelstring-
valuestring | number-
valueSuffixstring-
tooltipstring-
tooltipLabelstring"More info"
subtitlestring-
trendnumber | null-
trendGoodWhen"up" | "down""up"
mutedbooleanfalse
trendFormat(trend: number) => stringunsigned en-US percent, e.g. "12%"
trendLabelstring-
isLoadingbooleanfalse

Slots

Name Description
chartOptional sparkline or other small chart, rendered below the value/subtitle.

Guidelines

Do

  • Use for a single at-a-glance KPI in a dashboard grid - a count, a rate, a cost.
  • Set trendGoodWhen="down" for metrics where a decrease is the good outcome (open critical findings, MTTR).
  • Pass an already-formatted value (toLocaleString(), formatUsd(), etc.) - this component doesn't format numbers itself. Use valueSuffix for a unit that reads dimmer than the number ("/100" on a score).
  • Put a Sparkline in the #chart slot for a cost or volume whose recent shape matters more than one more number.
  • From an i18n'd app, also pass tooltipLabel (the translated "More info") so the tooltip trigger's accessible name is localized.
  • From an i18n'd app, pass trendFormat (a locale-aware percent formatter) and trendLabel (a translated "12% up"/"12% down") - the built-in badge text is an unsigned en-US percent, and without trendLabel a screen reader hears only the number, not the direction.

Don't

  • Don't drop it standalone outside a stat grid - like Table's cell renderers, it's meant to render alongside its siblings, not on its own.
  • Don't pass a signed/labelled trend string ("+12%") - pass the raw ratio (0.12) and let the component format and color it.
  • Don't look for a horizontal layout, a leading icon or a header link - a MetricCard is one vertical block. A linked or icon-led summary is a Card with title/icon, or a LinkCard.

Examples

Minimal

Label, value and the trend badge. The badge hides itself for a null, zero or non-finite trend.

Active repositories
128
12%
<template>  <MetricCard    label="Active repositories"    :value="activeRepoCount.toLocaleString()"    :trend="repoTrend"  /></template>

With tooltip and sparkline

tooltip explains the metric beside its label; subtitle names the comparison period; the chart slot takes a Sparkline. trend-good-when flips the badge color for a metric where less is better.

Total cost
$2,310
8%
Last 30 days
<template>  <MetricCard    label="Total cost"    :value="formatUsd(totals.costUsd)"    tooltip="Spend across every scan type in the selected range."    subtitle="Last 30 days"    :trend="costTrend"    trend-good-when="down"  >    <template #chart>      <Sparkline :data="daily" :value="(d) => d.costUsd" />    </template>  </MetricCard></template>

Value suffix and point delta

value-suffix renders a dimmed unit after the number; trend-format turns a raw point delta into the badge text instead of the default percent.

Security score
82/100
3
vs. last 30 days
<template>  <MetricCard    label="Security score"    :value="score ?? '-'"    :muted="score === null"    value-suffix="/100"    :trend="scoreDelta"    :trend-format="(delta) => String(Math.abs(delta))"  /></template>

Loading

is-loading swaps the value and subtitle for skeletons and reserves a band where the sparkline will be, so the card keeps its size when data lands.

Total cost
<template>  <MetricCard    label="Total cost"    :value="formatUsd(totals.costUsd)"    subtitle="Last 30 days"    :is-loading="isPending"  >    <template #chart>      <Sparkline :data="daily" :value="(d) => d.costUsd" />    </template>  </MetricCard></template>