# FatDigits (/docs/charts/fat-digits)

FatDigits renders the exact number and maps its magnitude to font _weight_: five ordinal tiers across your `domain`, or
three with `tiers={3}`. The weight is a redundant second channel, so the large values in a dense column pop
preattentively before you read any of them. Five is the most weight steps that stay discriminable at text size. Weight
is never the primary read, since it is ordinal and coarse and the numeral is right there. Always pass a `domain`:
without one a lone number has no tier to sit in and renders at the middle weight.

```tsx
<span
  style={{ display: "inline-flex", flexDirection: "column", alignItems: "flex-end", gap: 2 }}
  className="tabular-nums"
>
  <FatDigits value={1204} domain={[0, 2100]} summary={false} fontSize={16} />
  <FatDigits value={318} domain={[0, 2100]} summary={false} fontSize={16} />
  <FatDigits value={2100} domain={[0, 2100]} summary={false} fontSize={16} />
  <FatDigits value={76} domain={[0, 2100]} summary={false} fontSize={16} />
</span>
```

## Install

```tsx
import { FatDigits } from "@microcharts/react/fat-digits";

<FatDigits value={1204} domain={[0, 2100]} title="Revenue" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.

## Try it

```tsx
import { FatDigits } from "@microcharts/react/fat-digits/interactive";

<FatDigits
  value={1204}
  domain={[0, 2100]}
/>
```

## When to use it

Use it for a dense numeric table column you scan for the big ones, a KPI number that should carry its own magnitude, or
an amount in a sentence. For trends use Sparkline, for proportions Progress, and for comparisons MiniBar.

## Sizing

**a scannable table column (the hero)**

```tsx
{rows.map((v) => <FatDigits value={v} domain={[0, 2100]} />)}
```

**digit mode weights each digit by its own magnitude**

```tsx
<FatDigits value={1902} encode="digit" />
```

## Variants

```tsx
<FatDigits value={1902} encode="digit" />
<FatDigits value={1204} domain={[0, 2100]} tiers={3} />
```

`encode="digit"` weights each digit by its own magnitude instead, a redundancy that helps you scan long ids and amounts.
The idea is adapted from FatFonts, which encodes magnitude as glyph ink area using a custom font; shipping a font would
add a dependency, so weight tiers on the inherited font carry the ordinal here.

`format` also takes `Intl.NumberFormatOptions`, and the numeral follows the locale's own grouping and decimal marks.

```tsx
<FatDigits
  value={12040}
  domain={[0, 20000]}
  format={{ style: "currency", currency: "EUR", maximumFractionDigits: 0 }}
  locale="de-DE"
/>
```

## Edge cases

```tsx
// without a domain a lone number has no tier to sit in, so it
// renders at the middle weight rather than implying a magnitude it can't know
<FatDigits value={1204} />
```

```tsx
<FatDigits value={Number.NaN} domain={[0, 2100]} />
```

A non-finite value renders nothing, neither numeral nor weight, and reports **"No data."** rather than guessing a tier.
On a font without many weights the browser snaps to the nearest available face, so fewer of the tiers stay visually
distinct.

## Four homes

**In a sentence**

```tsx
<p>
  Revenue column scan{" "}
  <span className="mc-inline">
    <FatDigits value={2100} domain={[0, 2100]} summary={false} />
  </span>{" "}
  — Acme at 2.1M stands out.
</p>
```

**In a table cell**

```tsx
<td>
  <FatDigits value={row.value} domain={[0, 2100]} />
</td>
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">2.1M</span>
  <FatDigits value={2100} domain={[0, 2100]} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  Acme <FatDigits value={row.value} domain={[0, 2100]} />
</button>
```

## Accessibility

The accessible name is the exact value and its tier: **"1,204 — tier 3 of 5."** In digit mode it is the number alone,
since each digit carries its own weight. The interactive entry eases the weight to its new tier on variable fonts (it
snaps otherwise) with no layout shift, and announces the value and tier through a polite live region.

This chart is a single unit, so there is nothing to rove between: a click, tap, `Enter` or `Space` selects it
and fires `onSelect`, and no selection stays pinned. That is the scalar half of the shared
[interaction contract](/docs/accessibility#one-interaction-contract).

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `value` (required) | `number` | The number (always the exact value). |
| `domain` | `readonly [number, number]` | Maps value to a weight tier — always pass one. |
| `encode` | `"value" \| "digit"` | value weights the whole numeral; digit weights each digit by its own magnitude. |
| `tiers` | `3 \| 5` | Weight steps (default 5). |
| `fontSize` | `number` | Numeral type size in viewBox units (default 14) — here the numeral is the mark, so this sizes the chart. |
| `animate` | `boolean` | (interactive) Opt-in entrance motion when the chart mounts client-side — add `import "@microcharts/react/motion"` once. Inert on the server, on hydrated server HTML, and under `prefers-reduced-motion`. |

Plus the shared grammar — `data`, `domain`, `color`, `title`, `summary`, `format` — and the layout props (`width`, `height`, `className`, `style`) that every chart accepts. Interactive entries also share `animate` and `live`, and — wherever a chart has more than one navigable unit — `onActive`, `onSelect`, `selectedIndex` and `defaultSelectedIndex`; and — wherever the chart shows a hover value — `readout`. See [the shared grammar](/docs/quickstart#the-shared-grammar).
