Rate cell
The canonical Yuno 'rate' cell used inside Table rows (Insights → Chargebacks rate per provider, Approval rate per issuer, response-code rates, etc.). A short Progress bar visualizes the value on a 0–100 scale + a tabular numeric label to the right shows the exact percentage to two decimals. Purely display — pass a `value` and drop it into a <TableCell>.
Anatomy
flex items-center gap-3. Left: the kit Progress atom sized `h-1.5 flex-1 min-w-[50px]` (6px tall, filling the cell, never under 50px), as the design system's DataTablePercentageCell draws it. Right: a text-sm tabular-nums span in the text colour showing `{value.toFixed(1)}%`. tabular-nums keeps widths aligned across rows in a Table so the percentages line up.
- 1Container
flex items-center gap-3. Sized to fit naturally inside a <TableCell> without extra padding — the TableCell owns row padding.
- 2Progress bar
The kit <Progress> atom at `h-1.5 flex-1 min-w-[50px]`: 6px tall, as wide as the cell allows and never under 50px, the same as the design system's DataTablePercentageCell. Value is passed straight through.
- 3Percentage label
text-sm in the text colour, tabular-nums and whitespace-nowrap, as in the design system. Format is always `{value.toFixed(1)}%` (one decimal, the launch definition's 36.9%) so widths align across rows.
Recipes
A single RateCell out of context. Rare on its own — RateCell is designed for table rows — but useful for a compact stat in a small card or a filter chip.
<RateCell value={87.42} />Drop RateCell as the child of a <TableCell> in a Table row. tabular-nums on the label keeps the percentages vertically aligned even when values differ in digit count (0.4% vs 12.0% vs 100.0%).
| Provider | Chargebacks rate |
|---|---|
| Stripe | 0.4% |
| Adyen | 0.6% |
| Braintree | 1.1% |
| dLocal | 2.0% |
| Cybersource | 0.3% |
<Table>
<TableHeader>
<TableRow>
<TableHead>Provider</TableHead>
<TableHead>Chargebacks rate</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((r) => (
<TableRow key={r.provider}>
<TableCell>{r.provider}</TableCell>
<TableCell>
<RateCell value={r.rate} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>A rate per group against a comparison period, the table the backend contract asks for: the rate as a bar with its number, and the change in its own column, in points and coloured by meaning (rateChange from @/lib/prior-period). The period goes in that column's header. No tick on the bar for the prior value: on a 0–100 track a one-point move is under a pixel, so the column carries it.
| Provider | Approval rate | vs same week last month |
|---|---|---|
| Stripe | 93.3% | +1.20 pts |
| Adyen | 91.8% | -0.60 pts |
| dLocal | 89.4% | 0.00 pts |
| Braintree | 86.7% | -2.20 pts |
| PayU | 84.2% |
import { RateCell } from "@/components/ui/rate-cell";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { rateChange } from "@/lib/prior-period";
import { cn } from "@/lib/utils";
// The change's colour by what it means: good news, bad news, no change.
const toneClass = { good: "text-success", bad: "text-destructive", neutral: "text-muted-foreground" } as const;
<Table>
<TableHeader>
<TableRow>
<TableHead>Provider</TableHead>
<TableHead>Approval rate</TableHead>
<TableHead className="text-right">vs same week last month</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((r) => {
// Approval up is good news; for a decline or chargeback rate pass "down".
const change = rateChange(r.rate, r.previous, "up");
return (
<TableRow key={r.provider}>
<TableCell>{r.provider}</TableCell>
<TableCell><RateCell value={r.rate} /></TableCell>
<TableCell className={cn("text-right tabular-nums", toneClass[change.tone])}>{change.label}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>Import
Copy this import line at the top of the file where you compose this atom.
import { RateCell } from "@/components/ui/rate-cell";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number (required) | — | Percentage 0–100. Rendered as {value.toFixed(1)}% next to the bar, one decimal. Widths align across table rows because the label uses tabular-nums. |
| className | string | — | Extra classes on the outer flex container. |
Related
Cross-links to atoms and patterns you may reach for next.
- ProgressThe underlying atom. If you need the Progress bar without the % label, use Progress directly.
- TableThe canonical host — RateCell fits inside <TableCell> without extra padding.
- Country cellThe other canonical Table cell atom in the kit (flag + country name).