Components

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>.

Updated Oct 14, 2026 by Juan Pablo Turina

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.

87.4%
  1. 1
    Container

    flex items-center gap-3. Sized to fit naturally inside a <TableCell> without extra padding — the TableCell owns row padding.

  2. 2
    Progress 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.

  3. 3
    Percentage 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

Standalone

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.

87.4%
<RateCell value={87.42} />
Inside a Table (canonical)

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%).

ProviderChargebacks 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>
With the prior period

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.

ProviderApproval ratevs 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.

RateCell is a controlled display atom — pass `value` (0–100) and it renders. No state, no callbacks. If you need a different bar size, wrap it or clone the ~10-line source.
import { RateCell } from "@/components/ui/rate-cell";

Props

Everything else from the underlying HTML or Radix primitive is forwarded via ...props.

PropTypeDefaultDescription
valuenumber (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.
classNamestring—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).