Mischief

137 / Charts

Metric Card

A KPI card whose chart you can run along to read any day. Let go and the headline goes back to the total.

Gross volume

$120,071

Up 6.7%vs previous period

Refunds

498

Down 16.3%vs previous 30 days

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/metric-card
import { MetricCard } from "mischief-ui/metric-card"

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/metric-card/metric-card.tsx
"use client" import * as React from "react" import { cn } from "@/lib/utils" export type MetricPoint = {  /** A calendar day as YYYY-MM-DD, or any ISO date. Shown in UTC. */  date: string  value: number} export type MetricPeriod = { value: string; label: string } 

Usage

export function Volume() {
  const [period, setPeriod] = useState("30d")
  const { days, previousTotal } = useVolume(period)

  return (
    <MetricCard
      label="Gross volume"
      data={days}
      previous={previousTotal}
      format={{ style: "currency", currency: "USD" }}
      periods={[
        { value: "7d", label: "7d" },
        { value: "30d", label: "30d" },
        { value: "90d", label: "90d" },
      ]}
      period={period}
      onPeriodChange={setPeriod}
    />
  )
}

The headline and the change

The headline is the sum of the days unless you pass value, which is what you want for a rate or an average. previous is the same measure for the period before. The change is shown as a signed percentage with an arrow, and takes the destructive colour only when it moves against goodDirection.

<MetricCard
  label="Conversion"
  data={days}
  value={0.034}
  previous={0.031}
  format={{ style: "percent", maximumFractionDigits: 1 }}
/>

Reading a day

Moving a pointer over the chart, or dragging a finger along it, puts a line on that day and swaps the headline for that day's value, with its date where the change was. Leaving the chart puts the total back. Vertical swipes still scroll the page. With the keyboard the chart is one tab stop: the arrows step by a day, Page Up and Page Down by a week, Home and End to either end, and Escape lets go.

Periods

periods draws a segmented control. The card does not fetch anything, so swap data and previous yourself when onPeriodChange fires. It works controlled with period or uncontrolled with defaultPeriod.

Motion

When the headline changes, each digit rolls to its new value. While you scrub, the digits change at once so the number keeps up with your hand. With reduced motion every change is immediate.

API

labelstringWhat is measured. Names the card.
dataMetricPoint[]One point per day of the current period, oldest first.
date, value (MetricPoint)string, numberA day as YYYY-MM-DD, shown in UTC, and its value.
valuenumberThe headline. Defaults to the sum of the data.
previousnumberThe same measure for the period before. Omit it and no change is shown.
goodDirection"up" | "down"Which way is good news. Defaults to up.
comparisonLabelstringText after the change. Defaults to vs previous period.
formatIntl.NumberFormatOptionsHow the headline and every day are written.
dateFormatIntl.DateTimeFormatOptionsHow the day being read is written. Defaults to Tue, Oct 20.
localestringThe locale for numbers and dates.
periods (MetricPeriod){ value: string; label: string }[]Draws a segmented control with these choices.
periodstringThe selected period, when controlled.
defaultPeriodstringThe first selected period, when uncontrolled.
onPeriodChange(period: string) => voidRuns when a period is chosen.
colorstringAny CSS colour. Defaults to var(--chart-1), then var(--primary).
chartHeightnumberHeight of the chart in pixels. Defaults to 96.
data-state"idle" | "scrubbing"Written to the root for styling.
classNamestringClasses for placement and appearance.
...divPropsHTMLAttributesNative div attributes.

Accessibility

The card is a group named by its label. The headline's accessible text is always the total, so a screen reader is never read a blur of rolling digits. The chart is a slider whose value text names the day and its value, such as Tue, Oct 20: $4,255. The change pairs its colour with an arrow and a sign and is read as Up 6.7% rather than as a symbol. The period control is a radio group with arrow-key movement and a single tab stop.