# ChartContainer

> An accessible chart frame with title, legend, responsive sizing and a data-table alternative.

Category: Advanced · Status: beta · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/chart-container

ChartContainer is not a charting engine. It gives any chart a consistent frame: a title and description, a legend using the theme-aware chart colours, the measured width for responsive drawing, and a “View as table” toggle so the data is never locked inside graphics. Stance includes two small SVG charts built on it: BarChart and Sparkline.

## Import

```tsx
import { ChartContainer, BarChart, Sparkline } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### BarChart

```tsx
import { BarChart } from '@bindshq/stance-react';

const deployments = [
  { month: 'Jun', deployed: 120, failed: 9 },
  { month: 'Jul', deployed: 164, failed: 7 },
  { month: 'Aug', deployed: 151, failed: 12 },
  { month: 'Sep', deployed: 238, failed: 5 },
  { month: 'Oct', deployed: 260, failed: 4 },
  { month: 'Nov', deployed: 302, failed: 3 },
];

export function Bars() {
  return (
    <BarChart
      title="Signature deployments are accelerating"
      description="Mailboxes updated each month through the Exchange relay."
      data={deployments}
      categoryKey="month"
      categoryLabel="Month"
      series={[
        { key: 'deployed', label: 'Deployed' },
        { key: 'failed', label: 'Failed' },
      ]}
    />
  );
}
```

### Custom chart

Use the render function to draw your own SVG with the measured size and series colours.

```tsx
import { ChartContainer } from '@bindshq/stance-react';

const deployments = [
  { month: 'Jun', deployed: 120, failed: 9 },
  { month: 'Jul', deployed: 164, failed: 7 },
  { month: 'Aug', deployed: 151, failed: 12 },
  { month: 'Sep', deployed: 238, failed: 5 },
  { month: 'Oct', deployed: 260, failed: 4 },
  { month: 'Nov', deployed: 302, failed: 3 },
];

export function Custom() {
  const values = deployments.map((d) => d.deployed);
  const max = Math.max(...values);
  return (
    <ChartContainer
      title="Deployed mailboxes"
      series={[{ key: 'deployed', label: 'Deployed' }]}
      data={deployments}
      categoryKey="month"
      height={180}
    >
      {({ width, height, colors }) => {
        const step = width / (values.length - 1);
        const points = values
          .map((v, i) => `${i * step},${height - 8 - (v / max) * (height - 16)}`)
          .join(' ');
        return (
          <svg
            width={width}
            height={height}
            role="img"
            aria-label="Line chart: deployed mailboxes rose from 120 in June to 302 in November."
          >
            <polyline
              points={points}
              fill="none"
              stroke={colors[0]}
              strokeWidth={2}
              strokeLinejoin="round"
            />
          </svg>
        );
      }}
    </ChartContainer>
  );
}
```

### Sparkline

Word-sized trends for stat cards and tables, with a generated text summary.

```tsx
import { Sparkline } from '@bindshq/stance-react';

export function Sparklines() {
  return (
    <div style={{ display: 'grid', gap: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <span style={{ width: 160 }}>Active signatures</span>
        <Sparkline data={[42, 48, 51, 49, 60, 72, 81]} />
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <span style={{ width: 160 }}>Failed deliveries</span>
        <Sparkline data={[12, 9, 11, 7, 5, 4, 3]} area color="var(--st-color-chart-3)" />
      </div>
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| ChartContainer | `title` (required) | `ReactNode` | None | Visible title; names the figure. |
| ChartContainer | `description` | `ReactNode` | None | What the chart shows; linked with aria-describedby. |
| ChartContainer | `series` | `ChartSeries[]` | None | { key, label, color? } per series. Colours default to --st-color-chart-1…5. |
| ChartContainer | `data` | `ChartDatum[]` | None | Rows for the data table alternative. |
| ChartContainer | `categoryKey` | `string` | None | Key of the category column in data. |
| ChartContainer | `categoryLabel` | `string` | `'Category'` | Header of the category column. |
| ChartContainer | `height` | `number` | `240` | Chart area height in px. |
| ChartContainer | `children` (required) | `ReactNode \| ({ width, height, colors }) => ReactNode` | None | The chart, or a render function receiving the measured size. |
| ChartContainer | `formatValue` | `(value: number, series) => string` | None | Formats values in the table. |
| ChartContainer | `showLegend` | `boolean` | None | Defaults to true when there are series. |
| ChartContainer | `defaultView` | `'chart' \| 'table'` | `'chart'` | Initial view. |
| ChartContainer | `actions` | `ReactNode` | None | Extra header controls, such as a period selector. |
| ChartContainer | `headingLevel` | `2 \| 3 \| 4 \| 5 \| 6` | `3` | Title heading level. |
| BarChart | `data / categoryKey / series` (required) | `ChartDatum[] / string / ChartSeries[]` | None | Grouped vertical bars per category. |
| BarChart | `maxValue` | `number` | None | Fixed axis maximum; defaults to a rounded data maximum. |
| BarChart | `summary` | `string` | None | Overrides the generated accessible summary. |
| Sparkline | `data` (required) | `number[]` | None | Values, oldest first. |
| Sparkline | `width / height` | `number` | `120 / 32` | Size in px. |
| Sparkline | `area` | `boolean` | `false` | Fill under the line. |
| Sparkline | `color` | `string` | `'var(--st-color-chart-1)'` | Line colour. |
| Sparkline | `label` | `string \| null` | None | Overrides the generated summary; null hides it when adjacent text describes the trend. |

## Accessibility

- The container is a <figure> named by its title and described by its description.
- BarChart and Sparkline render role="img" with a generated summary, such as “Highest: Deployed in Sep, 260”.
- “View as table” replaces the chart with a real data table, the most reliable way for screen reader users to explore values.
- Chart colours come from the ink ramp and adapt to each theme. Pair colour with labels; never rely on colour alone.

## Keyboard

| Keys | Action |
| --- | --- |
| `Enter` `Space` | Toggles between chart and table views. |

## Guidelines

**Do**

- Write a title that states the insight, not just the metric.
- Always provide data for the table alternative.

**Don’t**

- Use more than five series in one chart.
- Use charts where a single number in a StatCard would do.

## Related

- https://stanceui.com/components/stat-card
- https://stanceui.com/components/table
