Stat
A big number with a short label and a signed change, where importance shows in size, weight and color.
Monthly visitors
24,608
+2,140 since September
Refunds
38
+6 since September
Installation
$ pnpm dlx shadcn@latest add @ssntl/statUsage
import { Stat, StatDelta, StatLabel, StatValue } from "@/components/ui/stat"
<Stat>
<StatLabel>Monthly visitors</StatLabel>
<StatValue>24,608</StatValue>
<StatDelta trend="up">+2,140 since September</StatDelta>
</Stat>- Importance: the value leads in size (48px, lg 64px), weight (bold) and color (foreground); label and note step back to muted.
- Beyond size: hierarchy comes from weight and color too, so the number still leads in a small card.
- Write the sign and the period in the delta; green and red only support the words.
- Peer stats share size, weight and label position. Tabular figures keep them aligned.
Code
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
// Essential Design `es-stat`: when the number is the point, it gets the space.
// Importance shows in more than size: the value is large, heavy and full
// foreground; the label and the note step back in caption size and muted
// color. Tabular figures so peer stats line up.
const statValueVariants = cva("font-bold tabular-nums", {
variants: {
size: {
md: "text-headline",
lg: "text-stat",
},
},
defaultVariants: { size: "md" },
})
function Stat({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="stat"
className={cn("grid content-start gap-1", className)}
{...props}
/>
)
}
function StatLabel({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
data-slot="stat-label"
className={cn("text-label font-normal text-muted-foreground", className)}
{...props}
/>
)
}
function StatValue({
size,
className,
...props
}: React.ComponentProps<"p"> & VariantProps<typeof statValueVariants>) {
return (
<p
data-slot="stat-value"
className={cn(statValueVariants({ size, className }))}
{...props}
/>
)
}
// Write the sign and the period ("+2,140 this month"): the color only
// supports a change the words already state.
function StatDelta({
trend = "neutral",
className,
...props
}: React.ComponentProps<"p"> & { trend?: "up" | "down" | "neutral" }) {
return (
<p
data-slot="stat-delta"
data-trend={trend}
className={cn(
"text-caption",
trend === "up" && "text-green-600 dark:text-green-400",
trend === "down" && "text-destructive",
trend === "neutral" && "text-muted-foreground",
className
)}
{...props}
/>
)
}
export { Stat, StatDelta, StatLabel, StatValue, statValueVariants }