Metric ticker

Pro

A strip of live KPIs that roll to new values digit by digit and flash their change; rest on one to pause the strip and open its recent trend as a sparkline.

Live · keyboard ready

Product health

  • A compact live readout of several KPIs above a dashboard, a status page or a trading view.
  • Wall screens and operations views where values change on their own and people glance at them.
  • A header strip that must fit anywhere from a phone to a wide monitor without redesign.
  • Use metric-card or stat-card for one or two headline numbers with room for context.
  • Use line-chart or sparkline when the trend over time is the main content, not a detail.
  • Avoid it for values that change once a day; the live treatment would promise more than it delivers.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { MetricTicker, type TickerMetric } from "@/components/arc/metric-ticker/metric-ticker"; export function HealthStrip({ stats }: { stats: LiveStats }) {  const metrics: TickerMetric[] = [    { id: "mrr", label: "Monthly recurring revenue", value: stats.mrr, format: { style: "currency", currency: "USD", maximumFractionDigits: 0 } },    { id: "active", label: "Active now", value: stats.activeUsers },    { id: "conversion", label: "Trial conversion", value: stats.conversion, format: { style: "percent", maximumFractionDigits: 1 }, delta: "absolute", caption: "Rolling 7 days" },    { id: "latency", label: "API p95", value: stats.p95, format: { style: "unit", unit: "millisecond" }, positive: "down", delta: "absolute", history: stats.p95History },  ];   return <MetricTicker metrics={metrics} label="Product health" />;}

MetricTicker

A strip of live KPIs for the top of a dashboard. Each value rolls to its new reading digit by digit, ones first and each higher place a beat later like a mechanical counter, and its change flashes beside it as an arrow and an amount, green or red by whether the move is good news for that metric, before settling into a quieter tone. Rest on a metric, focus it or tap it and a floating panel grows out of it with a sparkline of recent values that draws itself in, the trend and the low and high; moving to the next metric glides the same panel across. When the metrics do not fit, the strip becomes a slow marquee that glides to a stop under the pointer, can be dragged or flicked and eases back into its drift, or a plain horizontal scroll.

PropTypeDefaultDescription
metricsreadonly TickerMetric[]–The metrics in order: { id, label, value, format?, delta?, positive?, history?, caption? }. Pass a new array whenever a value changes; changes are matched by id.
metrics[].formatIntl.NumberFormatOptions–Number format for the value, such as currency, percent or a unit like { style: "unit", unit: "millisecond" }. Defaults to whole numbers.
metrics[].delta"percent" | "absolute""percent"How the change reads: relative to the previous value, or as the raw difference in the metric's own format. Absolute changes of a percent metric read in points, such as 0.3 pts.
metrics[].positive"up" | "down""up"Which direction is good news. Set down for latency, error rate or churn so a rise flashes red.
metrics[].historyreadonly number[]–Recent values, oldest first, for the sparkline. Leave it out and the ticker keeps its own history from the values it has seen.
metrics[].captionstring–Context at the top of the panel, such as Rolling 7 days. Defaults to Last n updates.
overflow"marquee" | "scroll""marquee"What happens when the metrics do not fit: a slow marquee that pauses on hover, focus and while a panel is open, or plain horizontal scrolling with snap points. Metrics that fit simply share the width.
speednumber32Marquee drift in pixels per second.
flashDurationnumber1600How long a change stays emphasised before the delta settles to its resting tone, in milliseconds.
historyLengthnumber24Points kept for the sparkline when history is not passed.
localestring"en-US"Formatting locale. Fixed by default so server and client render the same digits.
labelstring"Live metrics"Accessible name of the strip.
classNamestring–Class on the root element.
styleCSSProperties–Inline styles on the root element.
Tab
Moves focus into the strip, onto the last focused metric. Focus opens its panel and pauses the marquee, bringing the metric into view.
Arrow LeftorArrow Right
Moves focus to the previous or next metric, wrapping at the ends; the panel glides with it.
HomeorEnd
Moves focus to the first or last metric.
EnterorSpace
Pins the panel open, or closes a pinned panel.
Escape
Closes the panel.
  • The strip is a labelled region with a list of metrics. Each metric is a button named with its label, formatted value and change, such as API p95, 214 ms, up 2 ms, with aria-expanded and aria-controls pointing at the panel.
  • Only one metric is a tab stop (roving tab index), so the strip costs one Tab however many metrics it holds.
  • Changes pair an arrow and a signed amount with color, so good and bad news never rely on color alone.
  • Values are not announced on every tick, which would flood a screen reader on a live strip; they are read when a metric is focused.
  • The repeated copy that makes the marquee seamless is hidden from assistive technology and out of the tab order.
  • The sparkline is decorative; the panel states the trend in words and the low and high as text.
  • Digits roll like an odometer on the smooth spring, turning the way the value moved and wrapping 9 to 0. The ones settle first and each higher place follows 25ms later, so a change ripples right to left. A new digit column or a wider delta opens on a spring, never in one frame.
  • A change flashes for flashDuration: the delta's tint deepens, its arrow slides in from the direction of travel and a thin ring expands and fades once. Then the tint drains and the delta rests in a quieter tone until the next change.
  • The marquee eases its speed rather than stopping dead: resting on the strip glides it to a halt in about half a second, leaving lets it pick up again gently. Drag to scrub it; release hands your flick back to the loop, which decays into the steady drift.
  • The panel grows out of the metric that opened it on a morph spring, scaling from the metric's center. It follows its metric every frame, and moving to another metric glides it across while its content crossfades.
  • The sparkline draws itself in, the area fades up under it and the last point pops in once the line arrives.
  • Focusing a metric inside a moving marquee pulls it into view on the shortest path around the loop.
  • With reduced motion there is no marquee (overflow falls back to horizontal scroll), digits change in place, the panel fades without scaling and the sparkline appears drawn.
  • Metrics that fit share the width equally. When they no longer fit, the strip becomes a marquee (or a snapping horizontal scroll with overflow="scroll"); the switch is measured with a ResizeObserver, not a breakpoint.
  • The marquee feathers its ends with a mask while the border stays crisp.
  • Below 420px values drop to the base size and metrics tighten their padding.
  • The panel is at most 248px wide and is kept inside the component's box; leave room under the strip or let it float over the content below.
  • Touch: a tap pins a panel and pauses the strip; a press anywhere outside closes it. Horizontal drags scrub the marquee while vertical page scrolling still works.
  • The marquee is one requestAnimationFrame loop writing a single transform. It stops entirely when the strip is offscreen, when the tab is hidden and when the marquee is not needed.
  • The panel follows its metric with a frame loop that runs only while a panel is open.
  • Digit wheels, the flash and the panel are transforms and opacity only. The sparkline is one small SVG path redrawn when its data changes.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use it as the live headline of a dashboard, status page or trading style view: four to eight metrics people glance at while doing something else.
  • Set positive: "down" on any metric where lower is better (latency, errors, churn, cost). Getting this wrong flashes good news red.
  • Use delta: "absolute" for rates and durations, where 0.3 points or 8 ms means more than a percent of a percent; keep the default percent for money and counts.
  • Pass history when you already have a series from your backend; otherwise the ticker builds one from what it sees since mount.
  • Keep labels to two or three words. The strip is for scanning; the panel caption can carry the context.
  • Batch updates. One to a few changes a second reads as live; a new array every frame just flickers.

The full library index for assistants is at /llms.txt.