Source: https://monarchui.dev/components/delta-indicator/

Components · Charts

# DeltaIndicator

An up, down or flat change with sign, arrow and a spoken sentence; colour follows which direction is better.

@rookcom/monarch-react/charts

Component

## Better, worse and lower-is-better

## Usage examples

### Flat, no data and pill

## Purpose

Shows how a value moved between two periods, the same way everywhere: arrow, signed percentage or amount, an optional comparison, and green or red depending on whether that direction is good.

### Use when

- A period-over-period change next to a KPI, in a table cell or in a list row.
- Changes where down is good, such as costs, response time, churn or a ranking position.
- Generating the same wording for a chart summary or `aria-label` (`formatDelta`).

### Avoid when

- Showing a trend over many points: use a sparkline or line chart.
- Status that is not a change (healthy, failing): use StatusBadge.

## Design principles

- Meaning never depends on colour: the arrow, the sign (+ or a true minus) and the spoken direction carry it; colour only says better or worse.
- `goodDirection` decides colour, not the sign: a 12% fall in cost is green.
- Nothing to compare is not zero: a missing or zero baseline shows "No earlier data", not "+∞%" or "0%".
- A change that rounds to zero when written ("0%") is shown as "No change", never "+0%".
- Formatting is Intl.NumberFormat with the given locale; currency goes through Money.

## API reference

```tsx
import { DeltaIndicator, formatDelta } from '@rookcom/monarch-react/charts';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `DeltaIndicator` | component | server-safe | `.mn-delta-indicator` |
| `formatDelta` | utility | server-safe | – |

The ref and native `<span>` attributes go to the root.

`formatDelta(options)` returns `{ value, direction, tone, text, magnitude, label }`, the same text and spoken sentence the component renders, for chart summaries and exports.

Exported from `@rookcom/monarch-react/charts`.

Generated from the TypeScript declarations of `packages/react/src/components/charts/delta-indicator`.

## Properties

With `format="percent"` (the default) `value` is a ratio: 0.12 is 12%. Pass `current` and `previous` to have the change computed instead.

### `DeltaIndicator`

A change between two periods: direction arrow, signed value and an optional comparison. Colour follows `goodDirection`, so a fall in costs is green; the arrow, sign and spoken "up 12% vs last month, better" carry the meaning without colour.

| Prop | Type | Description |
| --- | --- | --- |
| `appearance` | `"pill" \| "plain"` | `plain` (default) is inline text; `pill` puts the change in a tinted chip. |
| `className` | `string` |  |
| `comparison` | `ReactNode` | The comparison period after the change, e.g. "vs last month". Shown muted and spoken. |
| `currency` | `string` | ISO 4217 code for `format="currency"`, e.g. `USD`. Required in that format. |
| `current` | `number \| null` | This period's value; with `previous`, the change is computed. |
| `flatThreshold` | `number` | Changes whose size is at or below this count as flat. Default 0. |
| `format` | `DeltaFormat` | Default `percent`. |
| `formatOptions` | `Intl.NumberFormatOptions` | Extra `Intl.NumberFormat` options, e.g. `{ maximumFractionDigits: 0 }`. |
| `formatValue` | `((magnitude: number) => string)` | Writes the size of the change yourself (no sign); replaces `format`, `unit` and `formatOptions`. |
| `goodDirection` | `DeltaGoodDirection` | Which direction is an improvement. Default `up`. |
| `hideIcon` | `boolean` | Hides the direction arrow. The sign and the spoken direction remain. |
| `id` | `string` |  |
| `labels` | `Partial<DeltaLabels>` | Words to show and speak; merged over the English defaults. |
| `locale` | `string` | BCP 47 locale. Default: the runtime locale. |
| `previous` | `number \| null` | The previous period's value. With `format="percent"` the change is `(current − previous) / \|previous\|`, and a `previous` of 0 has no baseline, so it shows `labels.noData`. Otherwise the change is `current − previous`. |
| `size` | `"sm" \| "md"` | `sm` (default) for card footers, `md` next to body text. |
| `unit` | `string` | Unit written after a `number` change, e.g. `pt` or `ms`. |
| `value` | `number \| null` | The change itself. With `format="percent"` it is a ratio (0.12 is 12%). `null` means there is nothing to compare with. Takes precedence over `current` and `previous`. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `format` | `'percent'``'number'``'currency'` | `percent` (default) writes a ratio as a percentage; `number` with an optional `unit` (pt, ms); `currency` through Money with `currency`. |
| `goodDirection` | `'up'``'down'``'neither'` | Which direction is an improvement. Default `up`. `neither` keeps the change neutral grey. |
| `appearance` | `'plain'``'pill'` | `pill` puts the arrow and value in a tinted chip (`data-appearance`). |
| `size` | `'sm'``'md'` | `sm` (default) for card footers and tables, `md` beside body text. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Up / down | `data-direction="up\|down"` | Arrow, signed value, visually hidden direction word and "better" or "worse". |
| Better / worse / neutral | `data-tone="positive\|negative\|neutral"` | Derived from the direction and `goodDirection`. |
| Flat | `data-direction="flat"` | Zero, within `flatThreshold`, or rounding to zero: a dash and "No change", neutral. |
| No data | `data-empty` | `value` null or not finite, `current` or `previous` missing, or a `previous` of 0 for a percentage: "No earlier data". |

## Interaction behaviour

- None. DeltaIndicator is read-only text.

## Keyboard support

Not interactive, so there is no keyboard behaviour.

## Accessibility

Role

none (inline text)

Accessible name

The visually hidden sentence, e.g. "up 12% vs last month, better".

- The arrow and the signed value are `aria-hidden`; a visually hidden "up 12%" or "down 8%" is read instead, so screen readers never say "plus" or skip a glyph.
- The comparison is real text, read after the change, followed by a hidden "better" or "worse" when a direction is better.
- Every word is replaceable through `labels` for translation.
- In forced-colours mode the pill keeps a border; the arrow and sign remain.

## Responsive behaviour

- The change never wraps; the comparison may wrap onto the next line in narrow cells.

## Theme support

- Better uses `--mn-color-success-strong`, worse `--mn-color-danger-strong`, neutral `--mn-color-text-muted`; all meet AA text contrast in light, dark and high-contrast themes.
- The pill tint is the matching `-subtle` token.

## Do and don't

### Do

- Set `goodDirection="down"` for costs, latency, churn, bounce rate and rank positions.
- Pass the comparison period ("vs last month") so the change is never ambiguous.
- Use `formatDelta` for chart summaries so the words match the indicator.

### Don't

- Do not compute a percentage against a zero baseline; let DeltaIndicator show "No earlier data".
- Do not invent a change when the product has no previous value: pass `null`.
- Do not use a status colour on its own to show direction.

## Related components

- [StatCard](https://monarchui.dev/components/stat-card/): A KPI tile that shows a DeltaIndicator through `change`.
- [Money](https://monarchui.dev/components/money/): Formats currency changes.
- [Sparkline](https://monarchui.dev/components/sparkline/): A trend over many points rather than one change.
- [StatusBadge](https://monarchui.dev/components/status-badge/): A state rather than a change.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Money](https://monarchui.dev/components/money/), [VisuallyHidden](https://monarchui.dev/components/visually-hidden/) |

## Testing coverage

- Signed percentage with spoken direction, comparison and quality; arrow hidden.
- goodDirection down (costs) and neither; computed percentage and absolute changes with a unit.
- Currency through Money and a custom formatter.
- Flat for zero, inside the dead band and when rounding to zero.
- No-data label for null, NaN, missing or zero baselines.
- Translated labels and locale; pill, size, hideIcon; ref forwarding; formatDelta output.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/delta-indicator; Cross-browser axe: /components/delta-indicator |

Tests: `packages/react/src/components/charts/delta-indicator`

## All example code

### Better, worse and lower-is-better

```tsx
import { DeltaIndicator } from '@rookcom/monarch-react/charts';

<DeltaIndicator value={0.124} comparison="vs last month" locale="en-US" />
<DeltaIndicator value={-0.052} comparison="vs last month" locale="en-US" />
<DeltaIndicator
  current={182}
  previous={240}
  format="number"
  unit="ms"
  goodDirection="down"
  comparison="vs last week"
  locale="en-US"
/>
<DeltaIndicator
  value={1250}
  format="currency"
  currency="USD"
  goodDirection="down"
  comparison="in spend"
  locale="en-US"
/>
```

### Flat, no data and pill

```tsx
import { DeltaIndicator } from '@rookcom/monarch-react/charts';

<DeltaIndicator value={0.0004} comparison="vs last week" locale="en-US" />
<DeltaIndicator current={14} previous={0} />
<DeltaIndicator value={0.08} appearance="pill" size="md" locale="en-US" />
<DeltaIndicator
  value={-3}
  format="number"
  goodDirection="neither"
  appearance="pill"
/>
```
