Composition Bar
A single horizontal stacked bar showing how a total breaks down across categories, with an aligned legend listing each category's value and share. Hovering a segment or a legend row highlights both. Pure CSS — no charting library required.
Basic
Pass data, category and value. Percentages are computed against the sum of all values.
- Equities$4,820,00047.4%
- Fixed Income$3,140,00030.9%
- Real Estate$1,260,00012.4%
- Cash$640,0006.3%
- Commodities$310,0003.0%
Sort Order
sortOrder reorders segments by value. Use "none" to preserve the input array order.
none
- Organic Search18,42052.4%
- Direct9,31026.5%
- Referral4,20512.0%
- Social2,1806.2%
- Email1,0403.0%
descending
- Organic Search18,42052.4%
- Direct9,31026.5%
- Referral4,20512.0%
- Social2,1806.2%
- Email1,0403.0%
ascending
- Email1,0403.0%
- Social2,1806.2%
- Referral4,20512.0%
- Direct9,31026.5%
- Organic Search18,42052.4%
Bar Height
Control the thickness of the bar with barHeight (pixels).
6px
12px
24px
Custom Colors
Override the palette with any ChartColor[] — or raw hex strings for brand colors.
Palette names
- Equities$4,820,00047.4%
- Fixed Income$3,140,00030.9%
- Real Estate$1,260,00012.4%
- Cash$640,0006.3%
- Commodities$310,0003.0%
Hex values
- Equities$4,820,00047.4%
- Fixed Income$3,140,00030.9%
- Real Estate$1,260,00012.4%
- Cash$640,0006.3%
- Commodities$310,0003.0%
Formatters
valueFormatter formats the raw value; percentFormatter formats the share. Both apply to the legend and the tooltip.
- Organic Search18.4k52%
- Direct9.3k26%
- Referral4.2k12%
- Social2.2k6%
- Email1.0k3%
Minimum Segment Width
minSegmentPercent inflates slices below the given percentage so they stay visible; the excess is reclaimed from the largest segment. The legend always shows the true percentage. Note the 0.0% Trial slice below.
0 (true widths)
- Enterprise$9,800,00079.5%
- Mid-Market$2,100,00017.0%
- SMB$420,0003.4%
- Trial$4,0000.0%
2 (floor applied)
- Enterprise$9,800,00079.5%
- Mid-Market$2,100,00017.0%
- SMB$420,0003.4%
- Trial$4,0000.0%
Legend & Tooltip
Turn either off with showLegend={false} or showTooltip={false}. With the tooltip off, hover dimming is disabled too.
Bar only (no legend)
Static (no tooltip)
- Equities$4,820,00047.4%
- Fixed Income$3,140,00030.9%
- Real Estate$1,260,00012.4%
- Cash$640,0006.3%
- Commodities$310,0003.0%
Fill Parent
fillParent stretches the component to the parent's height and spreads the legend rows evenly — useful inside fixed-height cards so the chart balances with its siblings.
default
- Equities$4,820,00047.4%
- Fixed Income$3,140,00030.9%
- Real Estate$1,260,00012.4%
- Cash$640,0006.3%
- Commodities$310,0003.0%
fillParent
- Equities$4,820,00047.4%
- Fixed Income$3,140,00030.9%
- Real Estate$1,260,00012.4%
- Cash$640,0006.3%
- Commodities$310,0003.0%
Empty State
When the data is empty or all values sum to zero, a placeholder bar is rendered. Pass emptyLabel to explain why.
No allocation data for this period.
CompositionBar
A single horizontal stacked bar showing how a total breaks down across categories, with an aligned legend listing each category's value and share. Segments and legend rows highlight together on hover, and a minimum segment width keeps tiny slices visible.
Usage
import { CompositionBar } from "@/components/charts/composition-bar"
// Basic composition
<CompositionBar
data={data}
category="assetClass"
value="amount"
valueFormatter={(v) => `$${v.toLocaleString()}`}
/>
// Sorted, custom palette
<CompositionBar
data={data}
category="source"
value="sessions"
sortOrder="descending"
colors={["violet", "amber", "rose", "teal", "cyan"]}
/>
// Bar only, no legend
<CompositionBar
data={data}
category="assetClass"
value="amount"
showLegend={false}
barHeight={8}
/>
// Fill a fixed-height card
<div className="h-64">
<CompositionBar
data={data}
category="assetClass"
value="amount"
fillParent
/>
</div>CompositionBar Props
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | Record<string, any>[] | — | Array of data objects. Each object needs a key for the category label and a key for the numeric value. |
| category* | string | — | Key in each data object used as the segment label (e.g. "assetClass", "source"). |
| value* | string | — | Key in each data object used as the segment's numeric value. Percentages are computed against the sum of all values. |
| colors | (ChartColor | string)[] | CHART_COLORS | Color palette for the segments. Accepts palette names or raw hex strings, and cycles if there are fewer colors than categories. |
| valueFormatter | (value: number) => string | v => v.toString() | Formats the raw value in the legend and tooltip. |
| percentFormatter | (percent: number) => string | p => `${p.toFixed(1)}%` | Formats the share percentage in the legend and tooltip. Always receives the true percentage, never the inflated render width. |
| sortOrder | "ascending" | "descending" | "none" | "none" | Sort order applied to segments by value. Use "none" to keep the order of the input array. |
| barHeight | number | 12 | Height of the bar in pixels. |
| minSegmentPercent | number | 0.5 | Floor (in percent) applied to rendered segment widths so tiny slices stay visible. The inflation is reclaimed from the largest segment so widths still sum to 100. |
| showLegend | boolean | true | Show the legend rows below the bar (color swatch, label, value, percentage). |
| showTooltip | boolean | true | Show the hover tooltip and enable the hover dimming of non-hovered segments. |
| fillParent | boolean | false | Stretch to fill the parent's height and distribute legend rows evenly across the available vertical space. Use inside fixed-height cards so the chart balances with siblings. |
| emptyLabel | string | — | Text rendered under the placeholder bar when data is empty or all values sum to zero. |
| className | string | — | Additional CSS classes on the outer wrapper. |