# Bar chart

Amounts side by side, such as a week of days or a month: plain bars, or bars stacked by kind with a legend, over a value axis and light grid lines.

Block: `smbk-bar-chart`. Status: experimental. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `type` | `bars`, `stacked` | `bars` | One series as plain bars, the current one darker; or up to eight series stacked in each bar in the categorical colors, the rest gathered as Other. |

Parts: `smbk-bar-chart__legend`, `smbk-bar-chart__body`, `smbk-bar-chart__y-axis`, `smbk-bar-chart__y-label`, `smbk-bar-chart__plot`, `smbk-bar-chart__grid`, `smbk-bar-chart__gridline`, `smbk-bar-chart__baseline`, `smbk-bar-chart__bars`, `smbk-bar-chart__bar`, `smbk-bar-chart__segment`, `smbk-bar-chart__x-axis`, `smbk-bar-chart__x-cell`, `smbk-bar-chart__x-label`, `smbk-bar-chart__table`, `smbk-bar-chart__data`

## Markup

```html
<div data-entrance="load" class="smbk-bar-chart smbk-bar-chart--type-bars">
  <ul class="smbk-bar-chart__legend">
    <li class="smbk-legend-item smbk-legend-item--swatch-series">
      <span class="smbk-legend-item__swatch" aria-hidden="true"></span>
      <span class="smbk-legend-item__label">Cost</span>
    </li>
  </ul>
  <div class="smbk-bar-chart__body" role="img" aria-label="Saturday cost the most this week, $352.">
    <div class="smbk-bar-chart__y-axis">
      <span class="smbk-bar-chart__y-label">$400</span>
      <span class="smbk-bar-chart__y-label">$300</span>
      <span class="smbk-bar-chart__y-label">$200</span>
      <span class="smbk-bar-chart__y-label">$100</span>
      <span class="smbk-bar-chart__y-label">$0</span>
    </div>
    <div class="smbk-bar-chart__plot">
      <div class="smbk-bar-chart__grid"><span class="smbk-bar-chart__baseline" style="top:100%"></span><span class="smbk-bar-chart__gridline" style="top:75%"></span><span class="smbk-bar-chart__gridline" style="top:50%"></span><span class="smbk-bar-chart__gridline" style="top:25%"></span><span class="smbk-bar-chart__gridline" style="top:0%"></span><div class="smbk-bar-chart__bars" style="--smbk-bar-chart-count:7"><span class="smbk-bar-chart__bar" title="Mon: $168" style="height:42%;--smbk-bar-chart-order:0"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series" style="flex-grow:168"></span></span><span class="smbk-bar-chart__bar" title="Tue: $220" style="height:55.00000000000001%;--smbk-bar-chart-order:1"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series" style="flex-grow:220"></span></span><span class="smbk-bar-chart__bar" title="Wed: $152" style="height:38%;--smbk-bar-chart-order:2"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series" style="flex-grow:152"></span></span><span class="smbk-bar-chart__bar" title="Thu: $280" style="height:70%;--smbk-bar-chart-order:3"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series" style="flex-grow:280"></span></span><span class="smbk-bar-chart__bar" title="Fri: $248" style="height:62%;--smbk-bar-chart-order:4"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series" style="flex-grow:248"></span></span><span class="smbk-bar-chart__bar" title="Sat: $352" style="height:88%;--smbk-bar-chart-order:5"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series" style="flex-grow:352"></span></span><span class="smbk-bar-chart__bar" title="Today: $120" style="height:30%;--smbk-bar-chart-order:6"><span class="smbk-bar-chart__segment smbk-bar-chart__segment--swatch-series smbk-bar-chart__segment--current" style="flex-grow:120"></span></span></div></div>
    </div>
    <div class="smbk-bar-chart__x-axis">
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--major">Mon</span>
      </span>
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--minor">Tue</span>
      </span>
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--major">Wed</span>
      </span>
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--minor">Thu</span>
      </span>
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--major">Fri</span>
      </span>
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--minor">Sat</span>
      </span>
      <span class="smbk-bar-chart__x-cell">
        <span class="smbk-bar-chart__x-label smbk-bar-chart__x-label--major">Today</span>
      </span>
    </div>
  </div>
  <table class="smbk-bar-chart__table"><caption>Spend per day, this week</caption><thead><tr><th scope="col">Period</th><th scope="col">Cost</th></tr></thead><tbody><tr><th scope="row">Mon</th><td>$168</td></tr><tr><th scope="row">Tue</th><td>$220</td></tr><tr><th scope="row">Wed</th><td>$152</td></tr><tr><th scope="row">Thu</th><td>$280</td></tr><tr><th scope="row">Fri</th><td>$248</td></tr><tr><th scope="row">Sat</th><td>$352</td></tr><tr><th scope="row">Today</th><td>$120</td></tr></tbody></table>
</div>
```

## React

```tsx
import { BarChart } from './design-system/components/bar-chart/bar-chart.js';

<BarChart type="bars" label="Spend per day, this week" summary="Saturday cost the most this week, $352." labels={["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Today"]} series={[{ name: "Cost", values: [168, 220, 152, 280, 248, 352, 120] }]} current={6} legend xAxis format={() => {}} />
```

## Stylesheet

```css
/* smbk-bar-chart — Bar chart. Figma "Bar chart" 7580:947.
 * The legend over the chart; then the value labels at the left and the plot beside them, light grid lines at the
 * round values and the baseline at zero; a label under each bar, or under some of them. Plain bars are in the
 * series color, the bar still counting (today's) a step darker; stacked bars hold up to eight series in the
 * categorical colors, the first at the foot, and the rest gathered in the muted gray. Each bar has small top
 * corners. Many bars, such as a month's, sit closer together. A narrow layout drops every second label under the
 * bars. The first time the chart comes into view its bars grow in, one after another; pointing at a bar fades the
 * others. Nothing takes focus, unless the bars are made focusable: then a focused bar takes the focus ring and the
 * others fade. */

.smbk-bar-chart {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-stack-md);
  min-width: 0;
}

/* The block frames the words it hides for screen readers, so they stay inside it wherever it is placed: without a
   frame they hang from the page itself and stretch it, past the end of an area that scrolls. No weight, so a parent
   that places the block keeps its say. */
:where(.smbk-bar-chart) {
  position: relative;
}

.smbk-bar-chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--smbk-space-stack-xs) var(--smbk-space-inline-xl);
  margin: 0;
  padding: 0;
}

/* The value labels, the plot and, under the plot, its labels. */
.smbk-bar-chart__body {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--smbk-space-stack-md) var(--smbk-space-inline-md);
  color: var(--smbk-color-text-subtle);
  font-family: var(--smbk-typography-body-xs-font-family);
  font-size: var(--smbk-typography-body-xs-font-size);
  font-weight: var(--smbk-typography-body-xs-font-weight);
  line-height: var(--smbk-typography-body-xs-line-height);
  letter-spacing: var(--smbk-typography-body-xs-letter-spacing);
  text-transform: var(--smbk-typography-body-xs-text-transform);
}

/* The value labels, highest first, spread from the top of the plot to its foot, so each is centred on its line. */
.smbk-bar-chart__y-axis {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
  height: var(--smbk-size-chart-plot-height);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.smbk-bar-chart__plot {
  position: relative;
  height: var(--smbk-size-chart-plot-height);
}

/* The scale runs between the middles of the top and bottom value labels: half a line of their text in. */
.smbk-bar-chart__grid {
  position: absolute;
  inset: calc(var(--smbk-typography-body-xs-font-size) * var(--smbk-typography-body-xs-line-height) / 2) 0;
}

.smbk-bar-chart__gridline,
.smbk-bar-chart__baseline {
  position: absolute;
  right: 0;
  left: 0;
  height: var(--smbk-border-width-hairline);
  background: var(--smbk-color-border-subtle);
  translate: 0 -50%;
}

.smbk-bar-chart__baseline {
  z-index: 1;
  background: var(--smbk-color-data-reference-line);
}

.smbk-bar-chart__bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  gap: var(--smbk-space-16);
}

/* Many bars, such as a month's, sit closer together; their labels keep the same spacing. */
.smbk-bar-chart__body--close .smbk-bar-chart__bars,
.smbk-bar-chart__body--close .smbk-bar-chart__x-axis {
  gap: var(--smbk-space-4);
}

/* A bar as tall as its total, its series stacked from the foot, each as tall as its share of the total. */
.smbk-bar-chart__bar {
  display: flex;
  flex: 1 1 0;
  flex-direction: column-reverse;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--smbk-radius-2) var(--smbk-radius-2) 0 0;
}

.smbk-bar-chart__segment {
  flex-basis: 0;
  min-height: 0;
  background: var(--smbk-color-data-categorical-1);
}

/* ---- each series' color ---- */

.smbk-bar-chart__segment--swatch-2 { background: var(--smbk-color-data-categorical-2); }
.smbk-bar-chart__segment--swatch-3 { background: var(--smbk-color-data-categorical-3); }
.smbk-bar-chart__segment--swatch-4 { background: var(--smbk-color-data-categorical-4); }
.smbk-bar-chart__segment--swatch-5 { background: var(--smbk-color-data-categorical-5); }
.smbk-bar-chart__segment--swatch-6 { background: var(--smbk-color-data-categorical-6); }
.smbk-bar-chart__segment--swatch-7 { background: var(--smbk-color-data-categorical-7); }
.smbk-bar-chart__segment--swatch-8 { background: var(--smbk-color-data-categorical-8); }
.smbk-bar-chart__segment--swatch-series { background: var(--smbk-color-data-series); }
.smbk-bar-chart__segment--swatch-muted { background: var(--smbk-color-surface-track); }
.smbk-bar-chart__segment--current { background: var(--smbk-color-data-series-hover); }

/* ---- the labels under the bars: each centred under its bar; among close bars the first and last kept inside ---- */

.smbk-bar-chart__x-axis {
  grid-column: 2;
  display: flex;
  gap: var(--smbk-space-16);
}

.smbk-bar-chart__x-cell {
  display: flex;
  flex: 1 1 0;
  justify-content: center;
  min-width: 0;
  min-height: calc(var(--smbk-typography-body-xs-font-size) * var(--smbk-typography-body-xs-line-height));
  white-space: nowrap;
}

.smbk-bar-chart__body--close .smbk-bar-chart__x-cell:first-child {
  justify-content: flex-start;
}

.smbk-bar-chart__body--close .smbk-bar-chart__x-cell:last-child {
  justify-content: flex-end;
}

/* The table, for screen readers only, unless the page shows it in the disclosure under the chart. */
/* .smbk-bar-chart__table: hidden from view, kept for screen readers and the keyboard, by the shared rule in visually-hidden/visually-hidden.css. */

/* A table takes the room its rows need whatever width and height it is given, so the hidden one is full size, only
   unseen. It hangs from the block's end corner, reaching back and up, the way a page never scrolls: it adds no room
   to the block or to any area that scrolls around it, and stays a table for screen readers. */
.smbk-bar-chart__table {
  inset-block-end: 0;
  inset-inline-end: 0;
}

/* A narrow space shows every second label under the bars, keeping the last. */
@container style(--smbk-layout: narrow) {
  .smbk-bar-chart__x-label--minor {
    display: none;
  }
}

/* ---- pointing at a bar ----
   Where there is a mouse, pointing at a bar (a stacked bar whole, every series in it) fades every other bar to the
   faded opacity, half, so the one pointed at stands out; the fade moves with the feedback purpose, under reduced
   motion too, since nothing moves. The wrapper marks the bar whose column the pointer is in, gaps and the room above
   a short bar included (`data-active`), so moving from bar to bar never brings every bar back in between; markup
   written by hand fades by the bar's own hover. Nothing is told by the fade alone: every value is in the table. Bars
   take no focus unless made focusable (above). At rest every bar is whole. */

@media (hover: hover) {
  .smbk-bar-chart__bars:has(> .smbk-bar-chart__bar:hover) > .smbk-bar-chart__bar:not(:hover),
  .smbk-bar-chart__bars[data-active] > .smbk-bar-chart__bar:not([data-active]) {
    opacity: var(--smbk-opacity-faded);
    transition-delay: 0s;
  }

  /* The bar pointed at comes back at once; with nothing pointed at, every bar waits the hover close delay first. */
  .smbk-bar-chart__bars:has(> .smbk-bar-chart__bar:hover) > .smbk-bar-chart__bar:hover,
  .smbk-bar-chart__bars[data-active] > .smbk-bar-chart__bar[data-active] {
    transition-delay: 0s;
  }

  .smbk-bar-chart__bar {
    transition: opacity var(--smbk-motion-feedback-duration) var(--smbk-motion-feedback-easing) var(--smbk-motion-hover-close-delay);
  }
}

/* ---- a bar that takes focus ----
   Bars made focusable (a tabindex on each, one of them 0, and each named by its figures) take the focus ring around
   the bar, just outside it, and a focused bar stands out as a pointed one does: every other bar fades to the faded
   opacity, on any screen, since a keyboard has no hover. The wrapper also marks the focused bar `data-active`. */

.smbk-bar-chart__bar:focus-visible {
  outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
  outline-offset: var(--smbk-focus-ring-offset);
}

.smbk-bar-chart__bars:has(> .smbk-bar-chart__bar:focus-visible) > .smbk-bar-chart__bar:not(:focus-visible) {
  opacity: var(--smbk-opacity-faded);
}

/* ---- motion: the entrance ----
   The wrapper marks the chart `data-entrance` the first time it comes into view (simbolik/components/entrance.ts):
   waiting while it is out of view, every bar flat on the baseline; playing once it is seen, each bar growing up
   from the baseline with the chart entrance purpose, each starting a stagger step after the one before it, the
   step shrinking with many bars so the last still starts within the stagger span. The wrapper writes each bar's
   place (--smbk-bar-chart-order) and the number of bars (--smbk-bar-chart-count) inline: data, not design values.
   Without the mark (markup written by hand, the rendered pictures, after the entrance) the bars stand at their
   height, and under reduced motion nothing moves. Only the bars' scale changes, so nothing around them shifts. */

@media screen and (prefers-reduced-motion: no-preference) {
  .smbk-bar-chart[data-entrance] .smbk-bar-chart__bar {
    transform-origin: bottom;
  }

  .smbk-bar-chart[data-entrance="waiting"] .smbk-bar-chart__bar {
    scale: 1 0;
  }

  .smbk-bar-chart:is([data-entrance="load"], [data-entrance="playing"]) .smbk-bar-chart__bar {
    animation: smbk-bar-chart-grow var(--smbk-motion-chart-entrance-grow-duration) var(--smbk-motion-chart-entrance-easing) backwards;
    animation-delay: min(
      calc(var(--smbk-bar-chart-order, 0) * var(--smbk-motion-chart-entrance-stagger)),
      calc(var(--smbk-bar-chart-order, 0) / max(1, var(--smbk-bar-chart-count, 1) - 1) * var(--smbk-motion-chart-entrance-stagger-span))
    );
  }
}

@keyframes smbk-bar-chart-grow {
  from {
    scale: 1 0;
  }
}

/* ---- high contrast (forced colors) ---- */
/* The plot keeps its colors, so each segment still matches its legend swatch; a focused bar's ring takes the
   palette's highlight. */

@media (forced-colors: active) {
  .smbk-bar-chart__plot {
    forced-color-adjust: none;
  }

  .smbk-bar-chart__bar:focus-visible {
    outline-color: Highlight;
  }
}
```

## Tokens it reads

```json
{
  "space": {
    "4": {
      "$type": "dimension",
      "$value": "4px",
      "$description": "Raw space step, 4 px. Only for fixed distances inside a component; spacing that follows density uses space/inset, stack or inline."
    },
    "16": {
      "$type": "dimension",
      "$value": "16px",
      "$description": "Raw space step, 16 px. Only for fixed distances inside a component; spacing that follows density uses space/inset, stack or inline."
    },
    "stack": {
      "md": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Gap between things stacked one above another, medium."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{space.4}}",
        "$description": "Gap between things stacked one above another, extra small."
      }
    },
    "inline": {
      "xl": {
        "$type": "dimension",
        "$value": "{{space.16}}",
        "$description": "Gap between things side by side, extra large."
      },
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things side by side, medium."
      }
    }
  },
  "color": {
    "text": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      }
    },
    "border": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.300}}",
        "$description": "Dividers and quiet edges."
      },
      "focus": {
        "$type": "color",
        "$value": "{{color.red.600}}",
        "$description": "Keyboard focus ring on controls."
      }
    },
    "data": {
      "reference-line": {
        "$type": "color",
        "$value": "{{palette.light.reference-line}}",
        "$description": "A threshold or target line across a chart. Light grey/800, dark grey/200. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
      },
      "categorical": {
        "1": {
          "$type": "color",
          "$value": "{{palette.light.categorical.1}}",
          "$description": "Categorical position 1 (blue). Fixed order, never cycled. Light blue/600, dark blue/400. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "2": {
          "$type": "color",
          "$value": "{{palette.light.categorical.2}}",
          "$description": "Categorical position 2 (orange). Light orange/500, dark orange/400. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "3": {
          "$type": "color",
          "$value": "{{palette.light.categorical.3}}",
          "$description": "Categorical position 3 (neutral). Light grey/700, dark grey/300. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "4": {
          "$type": "color",
          "$value": "{{palette.light.categorical.4}}",
          "$description": "Categorical position 4 (green). Light green/500, dark green/400. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "5": {
          "$type": "color",
          "$value": "{{palette.light.categorical.5}}",
          "$description": "Categorical position 5 (purple). Light purple/500, dark purple/300. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "6": {
          "$type": "color",
          "$value": "{{palette.light.categorical.6}}",
          "$description": "Categorical position 6 (teal). Light teal/600, dark teal/500. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "7": {
          "$type": "color",
          "$value": "{{palette.light.categorical.7}}",
          "$description": "Categorical position 7 (yellow). Light yellow/500, dark yellow/500. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        },
        "8": {
          "$type": "color",
          "$value": "{{palette.light.categorical.8}}",
          "$description": "Categorical position 8 (pink). Light pink/600, dark pink/300. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
        }
      },
      "series": {
        "$type": "color",
        "$value": "{{palette.light.series}}",
        "$description": "The single series colour; the same as categorical 1, so one series and the first of many agree. Light blue/600, dark blue/400. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
      },
      "series-hover": {
        "$type": "color",
        "$value": "{{palette.light.series-hover}}",
        "$description": "The single series, one step stronger, when pointed at or focused. Light blue/700, dark blue/300. Marks reach 3:1 on the plot background; never colour alone: direct labels, markers or patterns too."
      }
    },
    "surface": {
      "track": {
        "$type": "color",
        "$value": "{{color.grey.500}}",
        "$description": "The filled track of a switch that is off: 3:1 against the page so the switch shows where it is (decision C)."
      }
    }
  },
  "typography": {
    "body-xs": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "12px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    }
  },
  "size": {
    "chart": {
      "plot-height": {
        "$type": "dimension",
        "$value": "196px",
        "$description": "Height of a line or bar chart's plot, the grid lines and the marks between its value labels. A fixed frame height in Figma with no variable behind it; it should become one."
      }
    }
  },
  "border-width": {
    "hairline": {
      "$type": "dimension",
      "$value": "{{border-width.1}}",
      "$description": "Thin lines: dividers and almost every edge."
    }
  },
  "radius": {
    "2": {
      "$type": "dimension",
      "$value": "2px",
      "$description": "Raw corner step, 2 px. Semantic corners (radius/control, container) point here."
    }
  },
  "opacity": {
    "faded": {
      "$type": "number",
      "$value": 0.5,
      "$description": "Half see-through: the parts of a chart not pointed at, set back so the one pointed at stands out. 50%."
    }
  },
  "motion": {
    "feedback": {
      "duration": {
        "$type": "duration",
        "$value": "{{duration.150}}"
      },
      "easing": {
        "$type": "cubicBezier",
        "$value": "{{easing.expo-out}}"
      }
    },
    "hover": {
      "close-delay": {
        "$type": "duration",
        "$value": "{{duration.320}}",
        "$description": "From the pointer leaving the opener and panel to the panel closing; coming back within it keeps the panel open."
      }
    },
    "chart-entrance": {
      "grow-duration": {
        "$type": "duration",
        "$value": "{{duration.500}}",
        "$description": "How long each bar takes to grow to its height."
      },
      "easing": {
        "$type": "cubicBezier",
        "$value": "{{easing.steep-out}}"
      },
      "stagger": {
        "$type": "duration",
        "$value": "{{duration.50}}",
        "$description": "The wait added for each bar after the first."
      },
      "stagger-span": {
        "$type": "duration",
        "$value": "{{duration.500}}",
        "$description": "The longest the last bar waits: with many bars the wait per bar shrinks, so the last still starts within half a second."
      }
    }
  },
  "focus": {
    "ring": {
      "width": {
        "$type": "dimension",
        "$value": "{{border-width.2}}",
        "$description": "Thickness of the focus ring."
      },
      "offset": {
        "$type": "dimension",
        "$value": "{{space.2}}",
        "$description": "Gap between a control and its focus ring."
      }
    }
  }
}
```

## Purpose

Shows amounts side by side, such as a week of days or a month, so a reader compares them by the height of their bars. Stacked, each bar splits its amount by kind, such as spend by product per day.

## Anatomy

- The block: the legend, the body and the table, one under the other.
- The legend part: a list of legend items, one for the series of plain bars, or one for each kind of stacked bars in the order they stack.
- The body part, the picture: the y-axis part with a y-label part for each round value, highest first; the plot part beside it; the x-axis part under the plot, an x-cell part under each bar holding its x-label part when shown.
- In the plot, the grid part: a gridline part at each round value and the baseline part at zero, and the bars part holding a bar part for each label. A bar holds a segment part for each kind, the first at the foot; a segment's color is a modifier of the part, and the current bar's segment is darker.
- The table part: every value in a table, for screen readers; or the data part, the same table in a closed disclosure under the chart.

## When to use

- Amounts for each day, week or month, where the reader compares one with another.
- The same amounts split into two to eight kinds, where both the total and its parts matter.

## When not to use

- For the shape of a change over a long time. Use a [line chart](line-chart.md).
- For one total split into parts with no time. Use a [donut chart](donut-chart.md).
- For values below zero. The bars start at zero.
- For more than about forty bars. Group them into weeks or months.

## Behavior

The chart fills the width it is given; the plot keeps one height. The value axis runs from zero to a round number above the tallest bar, in about four round steps; give two charts the same highest value and their bars compare. Bars share the width evenly, and more than 14 bars sit closer together. With many bars, show every seventh label or so: the last label always shows. Plain bars draw the first series; the bar still counting, such as today's, can be drawn a step darker. Stacked bars draw up to eight kinds in the eight series colors, in a fixed order, and gather a ninth and the rest as Other, in gray. A series can name its own color (`swatch`, a number from 1 to 8), so it keeps it whatever its place: a kind keeps its color when a filter leaves another out or a day has none of it, and the series that name none take the colors left, in order; `swatch="muted"` gathers a series into Other. A kind with nothing in it, every value zero, is still in the legend unless `legendEmpty` is turned off; the table lists it either way. In a narrow layout every second label is left out, keeping the last. The first time the chart comes into view the bars grow up from the baseline, one after another, and later changes to the data land at once. Under reduced motion, and in markup written by hand, it appears at once. With a mouse, pointing at a bar fades the other bars, so the one pointed at stands out; a stacked bar fades or stands out as a whole. Pointing at a bar also shows the browser's own tooltip, with its label and value, and for a stacked bar each kind and the total; hand-written markup puts the same words in a `title` on the bar. Nothing takes focus unless the chart is made `focusable`: then the bars are one stop for the Tab key, the arrow keys move from bar to bar, and the focused bar takes the focus ring while the others fade, as when pointed at. A focusable bar is named by its figures instead of showing the browser's tooltip, which would be read a second time. `onActiveChange` tells the page the bar pointed at or focused, by its place in the labels, and `null` when there is none, so the page can show its figures in a readout under the chart. The wrapper writes the values in English unless `locale` names another language or `format` writes them, so a page prepared on a server shows the same numbers once its script runs.

## Accessibility

### What it does for you

- The picture is an image named by the summary.
- Every value is in a table titled by the chart's label, a row for each bar and a column for each kind, with the total when stacked. The table lists each kind, even those gathered as Other in the picture.
- The table is out of sight, for screen readers, unless the page shows it in the disclosure under the chart, which the keyboard reaches.
- Stacked kinds differ by color, so the legend names them in the order they stack, from the foot up, and the table holds every value. The eight series colors are chosen to stay apart for readers with color blindness.
- The darker current bar is also named by its label, such as Today.
- In high contrast mode the plot keeps its colors, so each kind still matches the legend.
- The picture takes no focus, unless made focusable: then it is a group named by the summary, each bar an image named by its label, each kind's value and the total, one stop for the Tab key, and the axis words are left out, since every bar says its own.

### What you must do

- Write the summary as the chart's main message, in a sentence, such as which day cost the most.
- Show the table in the disclosure under the chart when sighted readers also need the exact values.
- In markup written by hand, name the body with `role="img"` and its summary, and keep the table. For bars that take focus, make the body `role="group"` named by the summary, hide the axes with `aria-hidden="true"`, give each bar `role="img"`, its figures in `aria-label` and a `tabindex`, `0` on one and `-1` on the rest, and move focus with the arrow keys yourself.
- When the bars take focus, show the figures of the bar pointed at or focused near the chart, from `onActiveChange`: sighted readers no longer get the browser's tooltip.

### How to test

- With a screen reader, the chart is read as an image named by its summary, and its table is found by the chart's label, with a row for each bar.
- With the keyboard, Tab to the disclosure when the page shows the table, then open and close it.
- With bars that take focus, Tab once to reach the bars, move with the arrow keys, and hear each bar's label, kinds and total; Tab again leaves the chart.
- In a forced-color mode, each kind in the plot still matches its legend entry.
- With text at 200% and the page 320 pixels wide, the legend wraps and the axis labels stay readable.

### Keyboard

| Key | What it does |
|---|---|
| Tab | Moves focus to the bars when they take focus, to the bar focused last or else the first, then to the disclosure under the chart when the page shows the table. Otherwise the picture takes no focus. |
| Right arrow or Down arrow | On a bar, moves focus to the next bar. |
| Left arrow or Up arrow | On a bar, moves focus to the bar before. |
| Home or End | On a bar, moves focus to the first or the last bar. |
| Enter or Space | Opens the focused disclosure, or closes it when it is open. |

## Usage rules

### Do

- Say in the label what is counted and over what time, and in the summary what it shows.
- Keep a stack to a few kinds, the largest first, and name them in the legend.
- Mark the bar still counting, and label it so.
- Give each kind its own color when a filter or the days can leave kinds out, so a kind keeps its color on every chart of the page.

### Don't

- Stack more than eight kinds: the rest become Other.
- Leave out the legend on stacked bars.
- Put a value only in the picture: the table holds them all.
