Interactive Answers
Diverging Bars
Stacked daily columns with one series hanging below zero on its own scale — triaged vs escalated, solved vs handed off.
2 variantsMIT
Preview
Alerts by source
Security agent · last 30 days
34,728alerts triaged in 30 days
99.3% closed without a human
| Label | Identity | Endpoint | Cloud | Network | SaaS | Escalated | Total | |
|---|---|---|---|---|---|---|---|---|
| Sep 12 | 420 | 233 | 143 | 113 | 68 | 53 | 6 | 1k |
| Sep 13 | 251 | 150 | 99 | 84 | 53 | 44 | 5 | 681 |
| Sep 14 | 347 | 171 | 92 | 63 | 56 | 39 | 4 | 768 |
| Sep 15 | 548 | 375 | 178 | 175 | 81 | 79 | 7 | 1.4k |
| Sep 16 | 569 | 243 | 199 | 122 | 97 | 59 | 9 | 1.3k |
| Sep 17 | 481 | 300 | 205 | 178 | 72 | 63 | 8 | 1.3k |
| Sep 18 | 433 | 247 | 156 | 127 | 78 | 62 | 7 | 1.1k |
| Sep 19 | 610 | 288 | 146 | 166 | 85 | 55 | 9 | 1.4k |
| Sep 20 | 336 | 158 | 80 | 91 | 47 | 30 | 5 | 742 |
| Sep 21 | 375 | 202 | 120 | 92 | 54 | 41 | 4 | 884 |
| Sep 22 | 527 | 351 | 157 | 153 | 109 | 63 | 9 | 1.4k |
| Sep 23 | 565 | 239 | 196 | 118 | 94 | 57 | 9 | 1.3k |
| Sep 24 | 653 | 335 | 189 | 137 | 74 | 87 | 9 | 1.5k |
| Sep 25 | 430 | 243 | 152 | 123 | 75 | 60 | 7 | 1.1k |
| Sep 26 | 435 | 249 | 158 | 129 | 80 | 64 | 7 | 1.1k |
| Sep 27 | 313 | 133 | 109 | 67 | 53 | 32 | 5 | 707 |
| Sep 28 | 334 | 157 | 79 | 90 | 46 | 29 | 5 | 735 |
| Sep 29 | 422 | 235 | 145 | 115 | 69 | 54 | 6 | 1k |
| Sep 30 | 527 | 351 | 157 | 153 | 109 | 63 | 9 | 1.4k |
| Oct 1 | 439 | 253 | 162 | 133 | 83 | 67 | 8 | 1.1k |
| Oct 2 | 805 | 362 | 166 | 895 | 71 | 70 | 25 | 2.4k |
| Oct 3 | 612 | 290 | 148 | 169 | 87 | 57 | 9 | 1.4k |
| Oct 4 | 322 | 143 | 118 | 77 | 61 | 39 | 4 | 760 |
| Oct 5 | 374 | 201 | 120 | 92 | 53 | 40 | 4 | 880 |
| Oct 6 | 657 | 340 | 194 | 141 | 78 | 55 | 10 | 1.5k |
| Oct 7 | 568 | 241 | 198 | 121 | 96 | 58 | 9 | 1.3k |
| Oct 8 | 446 | 261 | 169 | 140 | 88 | 72 | 9 | 1.2k |
| Oct 9 | 603 | 280 | 234 | 159 | 80 | 85 | 8 | 1.4k |
| Oct 10 | 536 | 361 | 166 | 162 | 71 | 70 | 10 | 1.4k |
| Oct 11 | 339 | 162 | 84 | 95 | 50 | 33 | 5 | 763 |
Installation
Copies the component into components/ui and installs its dependencies and theme tokens.
pnpm dlx shadcn@latest add https://tuffui.com/r/diverging-bars.jsonUsage
Pass your own data and wire the callbacks to your agent runtime.
import { DivergingBars } from "@/components/ui/diverging-bars";
// A security agent's month: alerts by source, escalations hanging below.
export function TriageVolume({ report }: { report: TriageReport }) {
return (
<DivergingBars
title="Alerts by source"
subtitle="Security agent · last 30 days"
labels={report.days.map((d) => d.label)}
series={report.sources.map((s) => ({
id: s.id,
label: s.name,
values: report.days.map((d) => d.bySource[s.id] ?? 0),
}))}
below={{
id: "escalated",
label: "Escalated",
values: report.days.map((d) => d.escalated),
}}
headline={{
value: report.total.toLocaleString("en-US"),
caption: "alerts triaged in 30 days",
}}
aside={`${report.autoClosedPct}% closed without a human`}
onSelect={(i) => openDay(report.days[i].date)}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| labels* | string[] | — | One label per column, e.g. a date. Every few are drawn under the chart. |
| series* | DivergingSeries[] | — | Stacked upward as separate segments, first series at the bottom. Colors default to the palette by position. |
| below | DivergingSeries | — | One series that hangs below the zero line on its own smaller scale. Defaults to the negative color. |
| title* | string | — | Header title. |
| subtitle | string | — | Secondary line under the title. |
| headline | { value: string; caption?: string } | — | Big number above the chart with a caption. Rolls when it changes. |
| aside | ReactNode | — | Right side of the headline row, e.g. a secondary stat. |
| format | (value: number) => string | compact (1.2k) | Formats ticks, the tooltip and the hidden table. |
| onSelect | (index: number) => void | — | Called when a column is hovered, clicked or reached with the arrow keys. |
| className | string | — | Extra classes for the root element. |
type DivergingSeries = {
id: string;
label: string;
values: number[]; // one per label
color?: string; // any CSS color
};
// Legend chips toggle series; at least one stacked series stays on.
// The tooltip total is the stack; the series below is listed but not added in.