ComponentsStat Tile

Stat Tile

component

A KPI tile — big number, label, delta and an optional sparkline.

Recurring revenue
This month at a glance.
Monthly recurring
$48,210
12.4%
Active users
8,431
4.1%
Churn rate
2.8%
0.6%

Composes: Card · Sparkline · Badges & pills

Recipe CSS

This is the exact CSS your kit ships for Stat Tile — token-driven, so it re-themes with every knob. Get it (and the rest) from the configurator's Use kit panel.

/* === Stat tile (#111) ====================================================
 * Premium metric tile pattern. Two tiles fit in one gallery card; each tile
 * stacks: eyebrow label + icon box (top row) → big tabular number → sparkline
 * + delta pill (foot row). Sparkline draws in on mount via stroke-dasharray. */
.stat-tile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--k-s-10);
}
.stat-tile {
  display: flex;
  flex-direction: column;
  gap: var(--k-s-8);
  padding: var(--k-space, 16px);
  background: var(--k-surface-2);
  border-radius: var(--k-radius-md);
  border: var(--k-hairline, 1px solid var(--k-border));
  box-shadow: var(--k-shadow-tactile);
  min-width: 0;
}
.stat-tile__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-s-6);
}
.stat-tile__label {
  font-size: var(--k-type-caption);
  font-weight: var(--k-weight-medium);
  letter-spacing: var(--k-track-eyebrow);
  text-transform: uppercase;
  color: var(--k-fg-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stat-tile__icon {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: var(--k-radius-sm);
}
.stat-tile__icon--primary { background: var(--k-primary-soft); color: var(--k-primary-soft-fg); }
.stat-tile__icon--accent { background: var(--k-secondary-soft); color: var(--k-secondary-soft-fg); }
.stat-tile__icon svg { width: 12px; height: 12px; }
.stat-tile__value {
  font-size: var(--k-type-h2);
  font-weight: var(--k-weight-semibold);
  font-family: var(--k-font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--k-track-display);
  line-height: var(--k-leading-tight);
  color: var(--k-fg);
}
/* CP3 — the HERO stat (confident-pro gap #1: one focal point per surface). The
 * lead KPI spans the full grid and renders its value at the display tier (~48px),
 * so a dashboard reads as a bento with one feature metric instead of a uniform
 * 2×2 of equals. The number earns its focus through SIZE, not colour — accent
 * stays reserved for the one action (CP2). The Linear/Stripe insight move. */
.stat-tile--hero { grid-column: 1 / -1; }
.stat-tile--hero .stat-tile__value {
  font-size: var(--k-type-display);
  letter-spacing: -0.03em;
  line-height: 1;
  /* CP6 — deploy the dormant CP1 entrance: the hero KPI rises + fades + settles
     on mount (the "the number arrives" Linear/Stripe beat). --k-anim-rise is
     'none' at Motion=None and the global reduced-motion guard snaps it instant
     (k-rise uses both-fill, so it lands on the visible end-state, never stuck). It
     replays only on remount / Motion change, not on every config tweak. */
  animation: var(--k-anim-rise);
}
.stat-tile__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-s-8);
}
.stat-tile__spark {
  width: 60%;
  max-width: 80px;
  height: 22px;
  flex-shrink: 1;
}
.stat-tile__spark path {
  stroke-dasharray: 200;
  stroke-dashoffset: 200;
  animation: stat-spark-draw 600ms cubic-bezier(.05, .7, .1, 1) both;
  animation-delay: 240ms;
}
@keyframes stat-spark-draw {
  to { stroke-dashoffset: 0; }
}
.stat-tile__delta {
  display: inline-flex;
  align-self: flex-start; /* hug content — don't stretch to the column's full width */
  align-items: center;
  gap: var(--k-s-2);
  padding: var(--k-s-2) var(--k-s-6);
  border-radius: 999px;
  font-size: var(--k-type-eyebrow);
  font-weight: var(--k-weight-semibold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.stat-tile__delta svg { flex-shrink: 0; }
/* Delta tone modifiers — avoid per-instance inline colours. */
.stat-tile__delta--up { color: var(--k-success); background: var(--k-success-soft); }
.stat-tile__delta--down { color: var(--k-danger); background: var(--k-danger-soft); }
/* Clickable KPI tile — drills into a detail view (hover lift + a trailing
 * chevron that nudges right, "there's more behind this number"). */
.stat-tile--clickable { cursor: pointer; transition: border-color var(--k-dur-fast, 140ms) var(--k-ease, ease), box-shadow var(--k-dur-fast, 140ms) var(--k-ease, ease), transform var(--k-dur-fast, 140ms) var(--k-ease, ease); }
.stat-tile--clickable:hover { border-color: var(--k-state-border); box-shadow: var(--k-shadow-sm); transform: translateY(-1px); }
.stat-tile--clickable:focus-visible { outline: 2px solid var(--k-ring, var(--k-primary)); outline-offset: 2px; }
.stat-tile__drill { margin-left: auto; display: inline-flex; color: var(--k-fg-faint); transition: transform var(--k-dur-fast, 140ms) var(--k-ease, ease); }
.stat-tile--clickable:hover .stat-tile__drill { transform: translateX(2px); color: var(--k-fg-muted); }
@media (prefers-reduced-motion: reduce) { .stat-tile--clickable, .stat-tile__drill { transition: none; } }
/* Bare metric strip — joined cells in one box with internal hairlines (was
 * .statgrp). Reuses .stat-tile__value / .stat-tile__label; no per-cell card. */
.stat-tile-strip {
  /* Responsive: auto-fit as many equal columns as the width holds, wrapping
   * cleanly. The grid GAP shows the container line as the internal hairline —
   * so dividers need no nth-child math and survive any column count. */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--k-bw, 1px);
  background: var(--k-border);
  border: var(--k-hairline, 1px solid var(--k-border));
  border-radius: var(--k-radius-md);
  overflow: hidden;
}
.stat-tile-strip__cell { padding: var(--k-space, 16px); background: var(--k-surface); display: flex; flex-direction: column; gap: var(--k-s-2); }
/* Summary-band Fill — the ONE focal "state at a glance" strip per screen wears
 * the tactical wash (flagship doctrine): cells take --k-surface-fill, the grid
 * gap stays the hairline. White (default) Background ⇒ fill resolves to plain
 * --k-surface, so the modifier is a no-op until a tint is chosen. */
.stat-tile-strip--fill .stat-tile-strip__cell { background: var(--k-surface-fill); }