# BarChart

Discrete categories compared by length.

- Group: Charts
- Import: `import { BarChart } from '@misoto22/folio/charts'`
- Page: https://ui.misoto22.com/components/bar-chart/
- Related: area-chart, composed-chart, radial-chart

## When to reach for it

The categories are buckets rather than a continuum. If the axis is time and the reader is following a trend, an AreaChart or LineChart reads it faster.

## Anatomy

- **Figure frame** (required) — ChartFigure’s <figure>, named by title whether or not the page prints a heading above it.
- **Bars** (required) — <BarChart.Bar>, drawn through a custom shape: a transparent rectangle for the hit area, then the painted bar three pixels shorter than its slot so a stacked segment keeps a hairline of page between it and the one above. A bar shorter than that trim is floored at one pixel rather than taken to nothing, so a small count is never pixel-identical to an absent one.
- **Axes** — <BarChart.XAxis> and <BarChart.YAxis>, both flat by default — no tick line, no axis line. Every Recharts prop passes straight through, domain included, which is the door a truncated baseline comes in through.
- **Legend** — <BarChart.Legend>. With isClickable each entry is a real <button> carrying aria-pressed rather than a div with a handler, which is the difference between a filter a keyboard can reach and one it cannot.
- **Value labels** — <BarChart.Values>, a slot composed inside a bar. show defaults to last; all is for five or six bars where the exact figures are the point, and past that it is a table wearing a chart.
- **Hidden data table** — The sr-only table of the FULL data rather than of the brushed window, so a reader on the table is never shown less than the CSV export holds. hideDataTable removes it, and zero rows render nothing.
- **Empty state** — ChartEmpty, rendered in place of the plot when data is empty — a title, a reason and an optional action, so a filter that matched nothing is told apart from a load that failed. empty={false} keeps the bare axes instead.

## Best practices

### Do

- Reach for buffer on a period still open. It hatches the last ROW rather than the last bar on screen, so brushing back into the middle of the range hatches nothing — a month that closed in March is never drawn as still being counted.
- Leave the value axis anchored at zero. A bar encodes by LENGTH from the baseline, so a domain of ["dataMin", "dataMax"] passed through <BarChart.YAxis> turns a two percent gap into a doubled bar. This is the distortion a bar chart cannot survive and a LineChart can: a line encodes by slope, so clipping its domain rescales the reading rather than inventing one.
- Reach for orientation="horizontal" when the category names are long. The alternative is a tick label rotated under every column, and a rotated label is slower to read than the bar it names.
- Pass a tickFormatter to <BarChart.YAxis> under stackType="percent". Unlike AreaChart’s expanded stack, which swaps in percentTick itself, the bar chart’s axis keeps defaultTick — so a normalised chart reads 0 to 1 instead of 0% to 100%.
- Put the series that has to be compared across categories at the BASELINE of a stack. Only the bottom segment starts at zero; every band above it floats on the ones below, and reading a third band across twelve months is a comparison the eye cannot make. When that comparison is the point, group the bars instead.

### Don’t

- hideDataTable leaves no exact figure anywhere. defaultTick compacts at ten thousand and above, so the axis says 1.2M and so do the <BarChart.Values> labels; the sr-only table, where every cell is a full toLocaleString, was the only place the real number was written.
- Twenty bars at variant="default" is a wall rather than twenty values. stripped draws a 2px cap over a wash and stays countable at that density, which is the density it exists for.
- Do not close barCategoryGap up. The space between groups is the only thing telling a reader that two adjacent bars are two series rather than two categories, so a grouped chart with no category gap reads as a stacked one.

## Accessibility

- title is required; the rows are also rendered as a visually hidden table.
- Every bar carries an invisible full-height hit rectangle, so a 3px bar at the bottom of the scale is as easy to hit as a full-height one.
- A clickable legend entry is a real button with aria-pressed, not a div with a click handler.
- The staggered grow-in is anchored to the chart’s own start rather than to each bar’s mount, so a hover cannot replay it — and reduce-motion drops it entirely.

## BarChart

Discrete categories compared by length — the shape for "how much, per thing". Reach for `<AreaChart>` or `<LineChart>` when the axis is continuous and the reader is following a trend rather than comparing buckets.

### Props

- `config` (required) — `TConfig & ValidateKeys<TData, TConfig>`.
- `data` (required) — `TData[]`. The rows the chart draws. One entry per point, bar or category.
- `title` (required) — `string`. What the chart shows, in a sentence a reader could act on. Required, and announced to a screen reader even when it is not printed.
- `showTitle` — `boolean`. Prints the title above the plot instead of hiding it from sight.
- `description` — `ReactNode`. A line under the title — the unit, the window, the caveat.
- `children` (required) — `ReactNode`. The composed parts — axes, grid, tooltip, legend, and the marks themselves.
- `className` — `string`. Merged onto the figure, last, so a call site can size or space it.
- `chartProps` — `ComponentProps<typeof RechartsBarChart>`. Escape hatch onto the raw Recharts chart element.
- `stackType` — `BarStackType` default `'default'`. How several marks combine: side by side, stacked, or normalised to 100%.
- `orientation` — `BarOrientation` default `'vertical'`. Which way the bars run. Reach for `horizontal` when the category names are long enough to need rotating under a column.
- `barRadius` — `number` default `BAR_RADIUS`. The corner every `<BarChart.Bar>` inherits.
- `animationType` — `ChartRevealType` default `'forward'`. The grow-in order every `<BarChart.Bar>` inherits.
- `barGap` — `number`. Gap between bars inside one category.
- `barCategoryGap` — `number`. Gap between categories.
- `defaultSelectedDataKey` — `string | null` default `null`. The series lit on first render, when the chart keeps its own selection.
- `selectedDataKey` — `string | null`. The selected series, driven from outside. Give this and the chart follows it; leave it undefined and the chart keeps its own, starting from `defaultSelectedDataKey`.
- `onSelectionChange` — `(selectedDataKey: string | null) => void`. Fires when the selection changes, and with null when it is cleared.
- `isLoading` — `boolean` default `false`. Swaps the marks for an animated skeleton, keeping the measured height so the page does not jump when the data lands.
- `loadingBars` — `number`. How many bars the skeleton draws.
- `xDataKey` — `keyof TData & string`. The row field on the category axis. Needed by the brush and by the table view.
- `hideDataTable` — `boolean` default `false`. Drops the hidden table view. Only correct when the page prints the data itself.
- `empty` — `ChartEmptyProps | false`. What the chart shows when it has nothing to draw. Rendered instead of the plot whenever `data` is empty and the chart is not loading — the state a real dashboard reaches within a week, and the one an empty pair of axes is indistinguishable from a failed load. `false` keeps the axes, for a chart whose emptiness is itself the reading.

## Example — default

```tsx
import { BarChart, type ChartConfig } from '@misoto22/folio/charts'

<BarChart title="Visitors by month" config={config} data={data} xDataKey="month">
  <BarChart.Grid />
  <BarChart.XAxis dataKey="month" />
  <BarChart.YAxis />
  <BarChart.Legend />
  <BarChart.Tooltip />
  <BarChart.Bar dataKey="desktop" />
  <BarChart.Bar dataKey="mobile" variant="hatched" />
</BarChart>
```

## Example — fill variants

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { BarChart, type BarVariant, type ChartConfig } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={variant}
    onValueChange={(next) => next && setVariant(next as BarVariant)}
    aria-label="Fill variant"
  >
    {VARIANTS.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <BarChart title={`Visitors by month — ${variant}`} config={config} data={data}>
    <BarChart.Grid />
    <BarChart.XAxis dataKey="month" />
    <BarChart.Tooltip />
    <BarChart.Bar dataKey="desktop" variant={variant} />
  </BarChart>
</div>
```

## Example — stacking and orientation

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'

<div className="flex w-full flex-col gap-4">
  <div className="flex flex-wrap gap-3">
    <ToggleGroup
      type="single"
      value={stackType}
      onValueChange={(next) => next && setStackType(next as BarStackType)}
      aria-label="Stacking"
    >
      {STACKS.map((option) => (
        <ToggleGroupItem key={option} value={option}>
          {option}
        </ToggleGroupItem>
      ))}
    </ToggleGroup>

    <ToggleGroup
      type="single"
      value={orientation}
      onValueChange={(next) => next && setOrientation(next as BarOrientation)}
      aria-label="Orientation"
    >
      {ORIENTATIONS.map((option) => (
        <ToggleGroupItem key={option} value={option}>
          {option}
        </ToggleGroupItem>
      ))}
    </ToggleGroup>
  </div>

  <BarChart
    title={`Visitors by channel — ${stackType}, ${orientation}`}
    config={config}
    data={data}
    stackType={stackType}
    orientation={orientation}
  >
    <BarChart.Grid />
    {horizontal ? (
      <>
        <BarChart.XAxis />
        <BarChart.YAxis dataKey="channel" width={128} />
      </>
    ) : (
      <>
        <BarChart.XAxis dataKey="channel" />
        <BarChart.YAxis />
      </>
    )}
    <BarChart.Legend />
    <BarChart.Tooltip />
    <BarChart.Bar dataKey="desktop" variant="duotone" />
    <BarChart.Bar dataKey="mobile" variant="hatched" />
  </BarChart>
</div>
```

## Example — emphasis

```tsx
import { BarChart, type ChartConfig } from '@misoto22/folio/charts'

<div className="grid w-full gap-8 lg:grid-cols-3">
  <BarChart title="Glowing" showTitle config={config} data={data}>
    <BarChart.XAxis dataKey="month" />
    <BarChart.Bar dataKey="desktop" glowing />
  </BarChart>

  <BarChart title="Buffer — the last period is still open" showTitle config={config} data={data}>
    <BarChart.XAxis dataKey="month" />
    <BarChart.Bar dataKey="desktop" buffer />
  </BarChart>

  <BarChart title="Hover highlight" showTitle config={config} data={data}>
    <BarChart.XAxis dataKey="month" />
    <BarChart.Tooltip />
    <BarChart.Bar dataKey="desktop" variant="stripped" enableHoverHighlight />
  </BarChart>
</div>
```

## Example — interaction

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'

<div className="flex w-full flex-col gap-4">
  <div className="flex flex-wrap gap-3">
    <ToggleGroup
      type="single"
      value={variant}
      onValueChange={(next) => next && setVariant(next as ChartTooltipVariant)}
      aria-label="Tooltip ground"
    >
      {VARIANTS.map((option) => (
        <ToggleGroupItem key={option} value={option}>
          {option}
        </ToggleGroupItem>
      ))}
    </ToggleGroup>

    <ToggleGroup
      type="single"
      value={roundness}
      onValueChange={(next) => next && setRoundness(next as ChartTooltipRoundness)}
      aria-label="Tooltip corner"
    >
      {ROUNDNESS.map((option) => (
        <ToggleGroupItem key={option} value={option}>
          {option}
        </ToggleGroupItem>
      ))}
    </ToggleGroup>
  </div>

  <BarChart title="Visitors by month" config={config} data={data}>
    <BarChart.Grid />
    <BarChart.XAxis dataKey="month" />
    <BarChart.Legend isClickable />
    <BarChart.Tooltip variant={variant} roundness={roundness} defaultIndex={1} />
    <BarChart.Bar dataKey="desktop" isClickable />
    <BarChart.Bar dataKey="mobile" variant="hatched" isClickable />
  </BarChart>
</div>
```

## Example — brush

```tsx
import { BarChart, type ChartConfig } from '@misoto22/folio/charts'

<BarChart title="Visitors by day" config={config} data={data} xDataKey="day">
  <BarChart.Grid />
  <BarChart.XAxis dataKey="day" />
  <BarChart.Tooltip />
  <BarChart.Bar dataKey="desktop" variant="stripped" />
  <BarChart.Brush height={56} />
</BarChart>
```

## Example — loading

```tsx
import { BarChart, type ChartConfig } from '@misoto22/folio/charts'

<BarChart title="Visitors by month" config={config} data={[]} isLoading loadingBars={10}>
  <BarChart.Grid />
  <BarChart.Bar dataKey="desktop" />
</BarChart>
```

## Example — value labels

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { BarChart, type ChartConfig, type ValueLabelMode } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={show}
    onValueChange={(next) => next && setShow(next as ValueLabelMode)}
    aria-label="Which points are labelled"
  >
    {MODES.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <BarChart title="Visitors by month" config={config} data={data}>
    <BarChart.Grid />
    <BarChart.XAxis dataKey="month" />
    {/* The value axis compacts itself above four digits: 30.5K rather than
        30,500, which is the same fact in half the width. */}
    <BarChart.YAxis label="Visitors" />
    <BarChart.Bar dataKey="desktop" variant="stripped">
      <BarChart.Values show={show} />
    </BarChart.Bar>
  </BarChart>
</div>
```

## Example — sonify

```tsx
import { BarChart, formatNumber, type ChartConfig } from '@misoto22/folio/charts'

<BarChart title="Revenue by month" config={config} data={data} xDataKey="month">
  <BarChart.Sonify keys={['revenue']} formatValue={money} />
  <BarChart.Grid />
  <BarChart.XAxis dataKey="month" />
  <BarChart.YAxis tickFormatter={money} width={72} />
  <BarChart.Legend />
  <BarChart.Tooltip />
  <BarChart.Bar dataKey="revenue" variant="duotone" />
  <BarChart.Bar dataKey="refunds" variant="hatched" />
</BarChart>
```

## Example — toolbar

```tsx
import { BarChart, type ChartConfig } from '@misoto22/folio/charts'

<BarChart title="Visitors by month" config={config} data={data} xDataKey="month">
  <BarChart.Toolbar exports={['png', 'csv']} />
  <BarChart.Grid />
  <BarChart.XAxis dataKey="month" />
  <BarChart.YAxis label="Visitors" />
  <BarChart.Tooltip />
  <BarChart.Bar dataKey="desktop" variant="duotone" />
</BarChart>
```
