Components · Charts
StackedProportionBar
One horizontal bar split into proportions, with a legend of values and shares as a real list.
Pipeline stages
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>Usage examples
Storage capacity with a remainder
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
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
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>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 (
totallarger 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
tonefrom 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
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
labelnames 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-solidintent 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
totalandremainderLabelfor capacities so the empty part is explained. - Pin
colorwhen 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: One value against a target, or a quota meter.
- DonutChart: The same breakdown as a larger chart with a table.
- BarList: 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