Heartwood Health · How to Build

08 · Grouped Bars.

← components.html
all how-tos
08
Grouped Bars

Two periods × N categories

When the slope chart is too subtle and the audience expects bars. Q3 vs Q4, before vs after, control vs variant.

When to use

Same set of categories, exactly two periods or conditions, and you want the height comparison to be loud. For 3+ periods, switch to small multiples (a row of slope or line charts) — grouped bars at 3+ widths-per-category becomes noisy fast.

How it animates

Each bar transitions from height: 0 to its target percentage. Stagger 60ms within each pair (left bar before right bar) and 120ms between categories. The within-pair stagger is what tells the eye these two bars belong together before the next pair starts.

Data shape
volumeQuarterCompare: {
  leftLabel:  'Q3',
  rightLabel: 'Q4',
  leftColor:  'var(--primary-3)',
  rightColor: 'var(--primary)',
  categories: [
    { label: 'ER', left: 10500, right: 12600 },
    // any number of categories
  ],
}
Build pattern
/* CSS — note the nested flex */
.grouped-col  { flex: 1; flex-direction: column; }
.grouped-pair { flex: 1; align-items: flex-end; }
.grouped-bar  { flex: 1; height: 0; }
.visible .grouped-bar { height: var(--h); }
⚠ Watch

This component intentionally avoids the staggered-bars trap (see component 02). The trick: nested flex. .grouped-col stretches to the full container height; .grouped-pair with flex: 1 grabs the available cross-axis space and gives the bars a definite parent for percentage resolution. If you build a new bar variant, copy this nested structure rather than putting align-items: flex-end on the row directly.