Stacked Column Chart
A stacked column chart React component. Clicking a legend item collapses that layer while the remaining layers sink into place on a spring and the Y axis rescales with tweened ticks; hover or use the arrow keys for a per-column breakdown.
A column chart that splits every column into layers so you can read its composition. The legend at the top has one toggle per layer (color dot, name, total across all columns). Clicking one collapses that layer to zero height and fades it out while the layers above sink into place on a spring; the Y axis re-rounds to the largest visible stack, grid lines glide to their new positions and tick numbers tween to their new values. Hovering a column dims the others to half opacity and shows a tooltip listing each layer's value and share, top to bottom in stack order. It is hand-drawn SVG with no charting library, and its width follows the container.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add stacked-column-chart.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<StackedColumnChart/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/stacked-column-chart.en.jsonOr, once registries are configured in components.json, install it as @webberui/stacked-column-chart.
Usage
import { StackedColumnChart } from "@/components/ui/stacked-column-chart";
const series = [
{ key: "web", label: "官網" },
{ key: "store", label: "門市" },
{ key: "group", label: "團購" },
];
const data = [
{ label: "3 月", values: { web: 42, store: 31, group: 12 } },
{ label: "4 月", values: { web: 46, store: 29, group: 16 } },
{ label: "5 月", values: { web: 51, store: 33, group: 22 } },
];
<StackedColumnChart
series={series}
data={data}
formatValue={(v) => `${v} 萬`}
onHiddenKeysChange={(keys) => console.log("隱藏:", keys)}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
series | StackedColumnSeries[] | — | Layer definitions, stacked bottom to top in order |
data | StackedColumnDatum[] | — | Data for each column |
hiddenKeys | string[] | — | Controlled hidden layer keys; managed internally when omitted |
defaultHiddenKeys | string[] | [] | Initially hidden layers in uncontrolled mode |
onHiddenKeysChange | (hiddenKeys: string[]) => void | — | Called when hidden layers change; at least one layer always stays visible |
height | number | 300 | Plot height (px, including X labels); width follows the container |
showTotals | boolean | true | Whether to show the visible-layer total above each column |
formatValue | (value: number) => string | grouped digits | Value formatter shared by axis, totals and tooltip; add currency and units here |
stiffness | number | 260 | Spring stiffness; when set it is written to --wb-stacked-column-chart-stiffness |
duration | number | 600 | Entrance grow duration (ms); when set it is written to --wb-stacked-column-chart-duration |
ariaLabel | string | "堆疊長條圖" (Stacked column chart) | Accessible name used for the summary and the data table caption |
className | string | — | Merged into the outermost container |
StackedColumnSeries
| Field | Type | Default | Description |
|---|---|---|---|
key | string | — | Unique id; matches the keys of data[].values |
label | string | — | Name shown in the legend and tooltip |
color | string | — | Override this layer's color; defaults to --wb-stacked-column-chart-1 through -5 |
StackedColumnDatum
| Field | Type | Default | Description |
|---|---|---|---|
label | string | — | X-axis label |
values | Record<string, number> | — | Values per layer; missing or negative values count as 0 |
niceStackedScale(max) is also exported by name. It returns the same Y-axis max and ticks the chart uses, which helps when lining up other charts.
How it works
- Colors come from CSS variables:
--wb-stacked-column-chart-1through-5default to five teal steps from dark to light (#134e4a#0f766e#14b8a6#5eead4#ccfbf1), with a brighter set swapped in automatically in dark mode. Set those five variables on any ancestor to replace the whole palette; more than five layers cycle through it. The focus ring reads--wb-stacked-column-chart-accent(default#0d9488,#2dd4bfin dark mode). - Timing can be overridden too:
--wb-stacked-column-chart-stiffness(default 260, damping fixed at 30) drives the spring for collapsing layers and moving grid lines, and--wb-stacked-column-chart-duration(default 600ms) drives the entrance growth. Columns are staggered by 40ms and tick numbers tween over 400ms. - A collapsed layer is not removed from the DOM. It shrinks to zero height and rests at the top of the stack below it, so when shown again it grows from that exact spot and the stack order never changes.
- At least one layer always stays visible: when only one is left its legend button gets
aria-disabledand clicking does nothing; if controlled mode passes "all hidden", the first layer still shows. - The entrance only plays once, when the chart scrolls into view (
useInViewwith a -10% margin). The tooltip has a fixed width and is clamped inside the container so it never overflows the card.
Accessibility
- Legend items are native
buttonelements witharia-pressedreflecting whether the layer is visible; Enter / Space toggles them - The plot is focusable (
tabIndex=0): ← / → move between columns, Home / End jump to the ends and Esc dismisses the tooltip; the current column's breakdown is announced through anaria-liveregion - The SVG has
role="img"with anaria-labelsummary (column count, visible layers, highest total), plus a screen-reader-only data table with every layer's value and the visible total per column - Every focusable element shows a
focus-visiblering - When the user has reduce motion enabled, column growth, layer collapsing and tick tweens jump straight to their final state, and the tooltip keeps working
Event Bar Chart
A Gantt-like event bar chart React component with timed segments on swimlanes, panned by drag or arrow keys, an event card that expands on hover or focus, a self-contained SVG time axis and a screen reader data table.
Summary Line Chart
A React multi-line chart with a live summary column: lines draw in with a stroke animation, and as the pointer moves the summary's values and change rates roll digit by digit to that point in time. Click a summary row to hide a line and the Y axis re-tweens.