ComponentsData
Statistic
Shows a number that matters, such as emails sent today, with its label and how it has changed.
Import
import { Statistic, StatisticGroup } from "@booleanpress/ui/statistic"Usage
Give a label and a value; the value is written with Intl.NumberFormat in the provider's locale.
import { Statistic } from "@booleanpress/ui/statistic"
export function SentToday({ sent }: { sent: number }) {
return <Statistic label="Emails sent today" value={sent} trend="up" trendValue={0.12} helpText="since yesterday" />
}format is number (12,408), currency (with an ISO 4217 currency code), percent (the value is a fraction: 0.42 is 42%) or compact (12.4K); formatOptions lays any other Intl.NumberFormat option over it. A string value is shown as it is.
trend ("up" or "down") adds an arrow and trendValue, a fraction written as a percentage: green for a rise and red for a fall, the other way round with invertTrendColor for counts where less is better, such as bounces. Screen readers hear the provider's trendUp or trendDown text ("Up 12%"), not the arrow. helpText follows the trend. icon adds a decorative icon in a circle, coloured with iconClassName. loading shows a placeholder in place of the value.
variant="card" draws the statistic on a bordered card. StatisticGroup lays statistics out in a row that wraps to fit, as cards unless its variant is plain.
Examples
Basic
A label and a number, written in the provider's locale.
Currency
format="currency" with a currency code; formatOptions drops the cents.
import { Statistic } from "@booleanpress/ui/statistic"
export default function StatisticCurrency() {
return (
<div className="flex flex-wrap gap-12">
<Statistic label="Monthly revenue" value={48250.5} format="currency" currency="EUR" />
<Statistic label="Average order" value={64} format="currency" currency="USD" formatOptions={{ maximumFractionDigits: 0 }} />
</div>
)
}Compact
format="compact" writes 12,400 as 12.4K; format="percent" writes 0.428 as 42.8%.
import { Statistic } from "@booleanpress/ui/statistic"
export default function StatisticCompact() {
return (
<div className="flex flex-wrap gap-12">
<Statistic label="Subscribers" value={12400} format="compact" />
<Statistic label="Emails this year" value={3_870_000} format="compact" />
<Statistic label="Open rate" value={0.428} format="percent" />
</div>
)
}Trend
trend and trendValue add a coloured arrow and the change; invertTrendColor makes fewer bounces green.
import { Statistic } from "@booleanpress/ui/statistic"
export default function StatisticTrend() {
return (
<div className="flex flex-wrap gap-12">
<Statistic label="Delivered" value={11982} trend="up" trendValue={0.124} helpText="since last week" />
<Statistic label="Open rate" value={0.381} format="percent" trend="down" trendValue={0.021} helpText="since last week" />
<Statistic label="Bounces" value={86} trend="down" trendValue={0.3} invertTrendColor helpText="since last week" />
</div>
)
}With icon
variant="card" with an icon in a status colour.
import { MailCheckIcon } from "lucide-react"
import { Statistic } from "@booleanpress/ui/statistic"
export default function StatisticWithIcon() {
return (
<Statistic
variant="card"
label="Delivered today"
value={11982}
trend="up"
trendValue={0.124}
helpText="since yesterday"
icon={<MailCheckIcon />}
iconClassName="bg-success-tag text-success-tag-foreground"
className="w-full max-w-xs"
/>
)
}Group
StatisticGroup lays four delivery figures out as cards in a row that wraps.
import { InboxIcon, MailCheckIcon, MailWarningIcon, MousePointerClickIcon } from "lucide-react"
import { Statistic, StatisticGroup } from "@booleanpress/ui/statistic"
export default function StatisticGroupExample() {
return (
<StatisticGroup className="w-full">
<Statistic label="Sent" value={12408} icon={<InboxIcon />} iconClassName="bg-info-tag text-info-tag-foreground" />
<Statistic
label="Delivered"
value={0.966}
format="percent"
trend="up"
trendValue={0.004}
icon={<MailCheckIcon />}
iconClassName="bg-success-tag text-success-tag-foreground"
/>
<Statistic
label="Opened"
value={0.412}
format="percent"
trend="down"
trendValue={0.018}
icon={<MousePointerClickIcon />}
iconClassName="bg-secondary text-secondary-foreground"
/>
<Statistic
label="Bounced"
value={86}
trend="down"
trendValue={0.3}
invertTrendColor
icon={<MailWarningIcon />}
iconClassName="bg-warning-tag text-warning-tag-foreground"
/>
</StatisticGroup>
)
}Loading
loading shows a placeholder for each value while the figures load.
import { Statistic, StatisticGroup } from "@booleanpress/ui/statistic"
export default function StatisticLoading() {
return (
<StatisticGroup className="w-full">
<Statistic label="Sent" loading />
<Statistic label="Delivered" loading />
<Statistic label="Opened" loading />
<Statistic label="Bounced" loading />
</StatisticGroup>
)
}Sizes
sm, default and lg write the value at 16, 18 and 24 px.
import { Statistic } from "@booleanpress/ui/statistic"
export default function StatisticSizes() {
return (
<div className="flex flex-wrap items-start gap-12">
<Statistic size="sm" label="Small" value={12408} />
<Statistic label="Default" value={12408} />
<Statistic size="lg" label="Large" value={12408} />
</div>
)
}Accessibility
- Semantics
- A description list: the label is a
dtand the value add, so a screen reader pairs them. The trend and help text are a secondddof the same label. - Labels
- The arrow and the drawn percentage are hidden; screen readers hear the provider's
trendUportrendDowntext instead ("Up 12.4%"). The icon is decorative. While loading, the statistic isaria-busyand the value reads as the provider'sloadingtext. - Focus
- A statistic takes no focus.
- Known limits
- Colour says whether a change is good; the words say only up or down. Say in
helpTextwhen the direction alone could mislead. - A value that updates live is not announced. Wrap the statistic in your own
aria-liveregion if people must hear changes.
- Colour says whether a change is good; the words say only up or down. Say in
Keyboard
| Key | Behaviour |
|---|
API
Statistic
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | ReactNode | What the number counts, such as "Emails sent". | |
currency | string | The ISO 4217 code for format="currency", such as "EUR". Defaults to "USD". | |
format | "number" | "currency" | "percent" | "compact" | number | number (12,400), currency (with currency), percent (a fraction: 0.42 is 42%) or compact (12.4K). |
formatOptions | NumberFormatOptions | Intl.NumberFormat options laid over the format's own, such as { maximumFractionDigits: 0 }. | |
helpText | ReactNode | A short note after the trend, such as "since last week". | |
icon | ReactNode | An icon in a 32 px circle at the end. Decorative: the label says what the number is. | |
iconClassName | string | Classes for the icon's circle, such as bg-info-tag text-info-tag-foreground. | |
invertTrendColor | boolean | false | Colours a rise red and a fall green, for counts where less is better, such as bounces. |
loading | boolean | false | Shows a placeholder in place of the value and the trend while the number loads. |
size | "default" | "sm" | "lg" | The value's size: 16 px (sm), 18 px (default) or 24 px (lg). Defaults to the provider's controlSize. | |
trend | "up" | "down" | The direction of the change: an arrow, coloured, with the words "Up" or "Down" for screen readers. | |
trendValue | number | The size of the change as a fraction (0.12 is 12%), written as a percentage beside the arrow. | |
value | string | number | null | The number, written with format in the provider's locale; a string is shown as it is. | |
variant | "plain" | "card" | plain (the default) or card, a bordered card. Inside a StatisticGroup it defaults to the group's. |
StatisticGroup
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "plain" | "card" | card | The look of the statistics inside, unless one sets its own: card (the default) or plain. |
Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.
Data attributes: data-slot="statistic" (Statistic), data-slot="statistic-group" (StatisticGroup), and data-variant, data-size, data-trend.
Provider strings: loading, trendUp, trendDown (BooleanUIProvider's strings).
Theming
The label is --muted-foreground, the value --foreground in bold. A rise is --success-tag-foreground and a fall --destructive-tag-foreground, the deep text colours of the status tags, which keep 4.5:1 on the page and on the card. The card is --card with a --border edge, a 12 px radius and a small shadow; the icon circle is --secondary unless iconClassName says otherwise.
The tokens its classes read. Change their values in your theme, and it follows.
| Token | Used for |
|---|---|
--card | background |
--card-foreground | text |
--destructive-tag-foreground | text |
--foreground | text |
--info-tag | background |
--info-tag-foreground | text |
--muted-foreground | text |
--secondary | background |
--secondary-foreground | text |
--success-tag-foreground | text |