PlStat
One figure, and what has happened to it. A number on its own says what things are; a number with a movement beside it says whether that is going anywhere.
import { PlStat } from 'plass-ui';
<PlStat label="Revenue" value="£48,120" change={12.4} description="vs last month" />;import 'package:plass_ui/plass_ui.dart';
PlStat(
label: const Text('Revenue'),
value: const Text('£48,120'),
change: 12.4,
description: const Text('vs last month'),
);Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | ReactNode | — | What the figure is of. The line above it |
| value | ReactNode | — | The figure itself, already formatted. A node rather than a number, because the currency, grouping, decimals and locale are the page's decision |
| description | ReactNode | — | A line under the figure. What it is compared with, usually |
| icon | ReactNode | — | A glyph beside the label |
| change | number | — | How much it moved, as a percentage. Drawn with an arrow, and coloured by whether that is good news rather than by its sign |
| changeLabel | ReactNode | — | What the change says instead of the formatted percentage. For a figure that moved by a count |
| improvesWhen | 'up' | 'down' | 'up' | Which way is good news. down for churn, latency and cost |
| loading | boolean | false | Draws a skeleton where the figure will be |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The type scale of the figure and the words around it |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | The family the icon takes |
| densityshared | 'default' | 'compact' | 'default' | The space between the three lines, and nothing else |
| Prop | Type | Default | Description |
|---|---|---|---|
| label | Widget? | — | What the figure is of. The line above it |
| value | Widget? | — | The figure itself, already formatted. A widget rather than a number, because the currency, grouping, decimals and locale are the screen's decision and this package has no dependencies to guess with |
| description | Widget? | — | A line under the figure. What it is compared with, usually |
| icon | Widget? | — | A glyph beside the label |
| change | double? | — | How much it moved, as a percentage. Drawn with an arrow, and coloured by whether that is good news rather than by its sign |
| changeLabel | Widget? | — | What the change says instead of the formatted percentage. For a figure that moved by a count |
| improvesWhen | PlStatDirection | PlStatDirection.up | Which way is good news. down for churn, latency and cost |
| loading | bool | false | Draws a skeleton where the figure will be |
| sizeshared | PlassSize | PlassSize.md | The type scale of the figure and the words around it |
| colorshared | PlassColor | PlassColor.primary | The family the icon takes |
| densityshared | PlassDensity | PlassDensity.standard | The space between the three lines, and nothing else |
Every native <div> attribute passes straight through. What the shared axes mean across the library is in prop conventions.
improvesWhen
The one thing a naive version of this gets wrong. The colour of a movement is decided by whether it is good news, not by its sign. Churn going up is not good news, and a green arrow on it is a dashboard lying to somebody.
up is the default and is right most of the time. Set improvesWhen="down" on about a third of the figures a dashboard has: churn, a bounce rate, a p95 latency, a support backlog, a cost.
value takes a node
Not a number, and deliberately. How a figure is written (the currency, the grouping, the decimals, the locale) is the page's decision, and Intl.NumberFormat already makes it. A component that took a number would have to guess at all four.
<PlStat
label="Revenue"
value={new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(total)}
/>The same, with package:intl doing the formatting. This package has no dependencies to do it with, which is the other half of the reason value is a widget.
PlStat(
label: const Text('Revenue'),
value: Text(NumberFormat.simpleCurrency(locale: 'en_GB').format(total)),
);PlStat.formatChange is the one number the widget does write: at most one decimal, and a sign on a rise. Anything more particular is what changeLabel is for.
Examples
changeLabel
For a figure that moved by a count rather than by a proportion.
<PlStat label="Sign-ups" value="1,204" change={8.1} changeLabel="+94 this week" />change still decides the arrow and the colour; changeLabel only decides the words.
loading
Draws a skeleton where the figure will be, and holds the change back with it. A movement beside a figure nobody has yet is a movement of nothing.
<PlStat label="Revenue" loading={pending} value={total} change={delta} />Notes
- It draws no surface. A figure sits in a
PlCardor in a row of them, and a sheet inside a sheet is two sheets. - The figure is
tabular-nums, so a row of stats that updates on a timer does not jitter as the digits change width.
Accessibility
- The arrow is
aria-hiddenand the sign is in the text. "+12.4%" reads correctly on its own, and a screen reader is not told about a triangle. - The colour is never the only thing carrying the direction, for the same reason: the sign and the arrow both say it.
- It has no role and no heading. A row of figures is a set of
<div>s to a screen reader unless the page says otherwise. Put them in a list, or give the row a<h2>, depending on what the page is.
The arrow is inside an ExcludeSemantics and the sign is in the text, so a screen reader hears "+12.4%" and not a triangle.