Source: https://monarchui.dev/components/stacked-proportion-bar/

Components · Charts

# StackedProportionBar

One horizontal bar split into proportions, with a legend of values and shares as a real list.

@rookcom/monarch-react/charts

Component

## Pipeline stages

## Usage examples

### Storage capacity with a remainder

### Status mode: monitors by state

### Compact, legend for screen readers only

## Purpose

Shows how one whole divides (pipeline stages, storage by type, monitors by state) in a single compact bar, with the numbers in a legend that also serves as the accessible version.

### Use when

- A breakdown inside a card or row where a donut would be too large.
- Usage of a capacity (`total` larger than the parts, with a "Free" remainder).

### Avoid when

- One value against a target: use Progress (meter).
- Ranking or comparing many parts: use BarList.
- Breakdowns over time: use a stacked BarChart.

## Design principles

- The bar is decorative; the legend is a real list ("Replied 18 · 9%"), so nothing depends on colour.
- Series colours come only from the data palette, in order or pinned. The one exception is status mode: when every segment is a state (operational, degraded, down), each takes a status `tone` from the intent tokens instead.
- Palette and status colours never mix in one bar: the types reject it, and a mixed array from untyped code logs a development warning and falls back to the palette.
- Small parts stay visible: segments have a minimum width and shares under 1% read "<1%".
- Gaps separate neighbours, so adjacent segments never merge.

## API reference

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

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `StackedProportionBar` | component | server-safe | `.mn-color-success-solid` |
| `formatShare` | utility | server-safe | – |

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

`formatShare(part, total, { locale, maximumFractionDigits })` is exported for the same "<1%" / ">99%" wording elsewhere.

Exported from `@rookcom/monarch-react/charts`; the legend is the shared chart legend (`.mn-chart__legend`).

Generated from the TypeScript declarations of `packages/react/src/components/charts/stacked-proportion-bar`.

## Properties

Negative and non-finite values count as 0.

### `StackedProportionBar`

One horizontal bar split into proportions (pipeline stages, storage by type, monitors by state) with a legend of values and shares. The bar is decorative; the legend is a real list, so the data never depends on colour.

| Prop | Type | Description |
| --- | --- | --- |
| `label` required | `string` | Names the legend list (the accessible version of the bar), e.g. "Lead pipeline" or "Storage". |
| `segments` required | `readonly ProportionSegment[] \| readonly ProportionStatusSegment[]` | The parts, in display order. Negative values are treated as 0. Either every segment uses the data palette (`color`, or none), or every segment has a status `tone` (status mode); the two never mix. |
| `className` | `string` |  |
| `emptyLabel` | `string` | Shown instead of the legend when every value is 0. Default "No data yet". |
| `formatValue` | `ValueFormatter` | Formats values. Wins over `valueFormat`. |
| `hideZero` | `boolean` | Leave segments with a value of 0 out of the bar and legend. Default `true`. |
| `id` | `string` |  |
| `legend` | `boolean` | `true` (default) shows the legend; `false` keeps it for assistive technology only, visually hidden. |
| `legendContent` | `ProportionLegendContent` | What the legend shows after each label. Default `both` ("1,204 · 34%"). |
| `locale` | `string` | BCP 47 locale for values and percentages. |
| `minSegmentWidth` | `string` | Narrowest a non-zero segment is drawn, so tiny shares stay visible. Default `4px`. |
| `percentDigits` | `number` | Decimal places in percentages. Default 0 (shares under 1% read "<1%"). |
| `remainderLabel` | `string` | Legend name for the empty remainder when `total` exceeds the sum, e.g. "Free". Omit to leave it out of the legend. |
| `size` | `"sm" \| "md" \| "lg"` | Bar thickness: `sm` 4px, `md` (default) 8px, `lg` 12px. |
| `total` | `number` | The size of the whole. Default: the sum of the segments. Set it larger for a capacity (storage, quota); the rest is drawn as empty track. |
| `valueFormat` | `Intl.NumberFormatOptions` | `Intl.NumberFormat` options for values. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `size` | `'sm'``'md'``'lg'` | Bar thickness 4, 8 (default) or 12px (`data-size`). |
| `legendContent` | `'both'``'value'``'percent'``'none'` | What each legend entry shows after its label. |
| `segment.tone (status mode)` | `'success'``'warning'``'danger'``'info'``'neutral'` | Every segment gets an intent colour (`--mn-color-<tone>-solid`, `neutral` is `--mn-color-text-subtle`); the root has `data-mode="status"`. All segments or none. |
| `legend` | `'true'``'false'` | `false` hides the legend visually but keeps it for assistive technology. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Remainder | `data-remainder` | `total` above the sum leaves empty track; with `remainderLabel` it is listed in the legend. |
| Status mode | `data-mode="status"` | Every segment has a `tone`; segments carry `data-tone` and the legend swatches use the same intent colours. |
| Zero segments | – | Left out of the bar and legend unless `hideZero={false}`. |
| Empty | `data-empty` | Every value 0: an empty track and `emptyLabel` ("No data yet"). |

## Interaction behaviour

- Hovering a segment shows its label, value and share as a native tooltip.

## Keyboard support

Not interactive, so there is no keyboard behaviour.

## Accessibility

Role

list (the legend)

Accessible name

label

names the list, e.g. "Lead pipeline".

- The bar is `aria-hidden`; every part is a list item with its label, value and share.
- With `legend={false}` the list is visually hidden, not removed.
- The empty state is announced with the label ("Lead pipeline: No data yet").
- In forced-colours mode the track is outlined and segments are drawn in CanvasText, separated by gaps.

## Responsive behaviour

- The bar fills its container at any width; the legend wraps onto more lines.
- Minimum segment widths can exceed the bar only with many tiny parts; the overflow is clipped.

## Theme support

- Segments use `--mn-color-data-*`, contrast-checked per theme; in status mode the `-solid` intent tokens. The track is `--mn-color-bg-muted`.
- The remainder swatch matches the track, outlined with `--mn-color-border-strong`.

## Do and don't

### Do

- Name the whole with `label`, and keep segment labels short.
- Use `total` and `remainderLabel` for capacities so the empty part is explained.
- Pin `color` when the same stages appear elsewhere on the page.
- Use status mode only when every segment is a state, and keep a text label on each one (the legend or the hidden list always shows it).

### Don't

- Do not use status tones for categories that are not states (stages, file types): use the palette.
- Do not mix tones and palette colours in one bar, or rely on the tone colour alone to say which state a part is.
- Do not hide the legend unless the same numbers are visible nearby.
- Do not use it for more than about six parts.

## Related components

- [Progress](https://monarchui.dev/components/progress-bar/): One value against a target, or a quota meter.
- [DonutChart](https://monarchui.dev/components/donut-chart/): The same breakdown as a larger chart with a table.
- [BarList](https://monarchui.dev/components/bar-list/): Ranking parts against each other.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | None |

## Testing coverage

- Legend list of non-zero parts with value and share; decorative bar; proportional flex sizes.
- Palette colours by position and pinned slots; hideZero false.
- Status mode: intent colours on segments and swatches, text legend; mixed arrays rejected by the types and warned about at runtime.
- Capacity remainder with a named legend entry.
- Legend content modes and the visually hidden legend.
- "<1%" and ">99%" shares; formatShare edges.
- Minimum width and size; empty state; negative and NaN values; ref; SSR.

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

Tests: `packages/react/src/components/charts/stacked-proportion-bar`

## All example code

### Pipeline stages

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

<div style={{ width: '100%', maxWidth: '32rem' }}>
  <StackedProportionBar
    label="Lead pipeline"
    locale="en-US"
    segments={[
      { key: 'new', label: 'New', value: 1240 },
      { key: 'enrolled', label: 'In a campaign', value: 610 },
      { key: 'replied', label: 'Replied', value: 182 },
      { key: 'converted', label: 'Converted', value: 41 },
      { key: 'unsubscribed', label: 'Unsubscribed', value: 6 },
    ]}
  />
</div>
```

### Storage capacity with a remainder

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

<div style={{ width: '100%', maxWidth: '32rem' }}>
  <StackedProportionBar
    label="Storage"
    size="lg"
    total={100}
    remainderLabel="Free"
    legendContent="value"
    formatValue={(v) => `${v} GB`}
    segments={[
      { key: 'docs', label: 'Documents', value: 38 },
      { key: 'media', label: 'Media', value: 21 },
      { key: 'backups', label: 'Backups', value: 12 },
    ]}
  />
</div>
```

### Status mode: monitors by state

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

<div style={{ width: '100%', maxWidth: '32rem' }}>
  <StackedProportionBar
    label="Monitors by state"
    legendContent="value"
    segments={[
      { key: 'up', label: 'Operational', value: 42, tone: 'success' },
      { key: 'degraded', label: 'Degraded', value: 3, tone: 'warning' },
      { key: 'down', label: 'Down', value: 1, tone: 'danger' },
      { key: 'paused', label: 'Paused', value: 2, tone: 'neutral' },
    ]}
  />
</div>
```

### Compact, legend for screen readers only

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

<div style={{ width: '100%', maxWidth: '20rem' }}>
  <StackedProportionBar
    label="Campaign progress"
    size="sm"
    legend={false}
    segments={[
      { key: 'replied', label: 'Replied', value: 12 },
      { key: 'done', label: 'Completed', value: 30 },
      { key: 'active', label: 'In progress', value: 58 },
    ]}
  />
</div>
```
