# Chart

A Recharts wrapper that colors series from the chart tokens and adds a matching tooltip and legend.

```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@zenoxai/ui/components/chart"

function ChartDemo() {
  const data = [
    { day: "Mon", opened: 42, resolved: 38 },
    { day: "Tue", opened: 57, resolved: 44 },
    { day: "Wed", opened: 35, resolved: 49 },
    { day: "Thu", opened: 61, resolved: 52 },
    { day: "Fri", opened: 48, resolved: 55 },
    { day: "Sat", opened: 19, resolved: 21 },
    { day: "Sun", opened: 14, resolved: 12 },
  ]
  const config = {
    opened: { label: "Opened", color: "var(--chart-1)" },
    resolved: { label: "Resolved", color: "var(--chart-2)" },
  } satisfies ChartConfig

  return (
    <ChartContainer config={config} className="aspect-auto h-64 w-full">
      <BarChart data={data}>
        <CartesianGrid vertical={false} />
        <XAxis dataKey="day" tickLine={false} axisLine={false} tickMargin={8} />
        <YAxis tickLine={false} axisLine={false} width={32} />
        <ChartTooltip content={<ChartTooltipContent />} />
        <ChartLegend content={<ChartLegendContent />} />
        <Bar dataKey="opened" fill="var(--color-opened)" radius={2} />
        <Bar dataKey="resolved" fill="var(--color-resolved)" radius={2} />
      </BarChart>
    </ChartContainer>
  )
}
```

## Usage

```tsx
import { Bar, BarChart, XAxis } from "recharts"

import {
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
  type ChartConfig,
} from "@zenoxai/ui/components/chart"

const config = {
  opened: { label: "Opened", color: "var(--chart-1)" },
} satisfies ChartConfig

export function AlertsPerDay({ data }: { data: AlertsPerDayRow[] }) {
  return (
    <ChartContainer config={config} className="h-64 aspect-auto w-full">
      <BarChart data={data}>
        <XAxis dataKey="day" />
        <ChartTooltip content={<ChartTooltipContent />} />
        <Bar dataKey="opened" fill="var(--color-opened)" />
      </BarChart>
    </ChartContainer>
  )
}
```

[Recharts](https://recharts.org) 3 is an optional peer dependency of `@zenoxai/ui`. Install it
in the app that renders charts:

```bash
pnpm add recharts
```

Import the chart parts from `@zenoxai/ui/components/chart`. No other subpath imports Recharts,
so apps without charts do not need it. The root entry, `@zenoxai/ui`, re-exports this module:
an app that imports from the root needs Recharts installed even if it renders no chart.

You write the chart with Recharts components; the library adds three things:

- `ChartContainer` makes the chart responsive, styles the axes, grid and cursor with the theme
  tokens, and turns each key of `config` into a CSS variable, `--color-<key>`, scoped to the
  chart. Series reference it: `fill="var(--color-opened)"`.
- `ChartTooltipContent` and `ChartLegendContent` render the tooltip and legend with the labels
  from `config`, so the data can keep short keys.
- `ChartConfig` types the config.

Give `ChartContainer` a height. It defaults to a 16:9 aspect ratio; replace it with
`aspect-auto` and a height class for a fixed-height chart, or `aspect-square` for a donut.

## Colors

Set `color` to one of the five chart tokens. They have light and dark values, so the chart
follows the theme with no extra config. Assign them in order, `--chart-1` first, and keep the
same color for the same series across the charts of a page.

| Token          | Color                                                      |
| -------------- | ---------------------------------------------------------- |
| `--chart-1`    | Blue, the brand primary                                    |
| `--chart-2`    | Green                                                      |
| `--chart-3`    | Orange                                                     |
| `--chart-4`    | Gray                                                       |
| `--chart-5`    | Light blue, a deeper step of the brand accent on light     |
| `--chart-line` | Red, for a line drawn over the series, such as a threshold |

Every chart color reaches 3:1 against the surface in both themes, as WCAG asks of a data mark.

For a series that needs different colors per theme outside the tokens, pass
`theme: { light, dark }` instead of `color`. Do not use the chart tokens for severity: severity
has its own scale, used by [SeverityBadge](/patterns/severity-badge).

## Examples

### Area chart

An area chart suits one series over a long period, such as alerts per day over two weeks.
`minTickGap` on the axis drops labels that would collide.

```tsx
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@zenoxai/ui/components/chart"

function ChartArea() {
  const data = [
    { date: "Oct 10", alerts: 132 },
    { date: "Oct 11", alerts: 98 },
    { date: "Oct 12", alerts: 87 },
    { date: "Oct 13", alerts: 154 },
    { date: "Oct 14", alerts: 171 },
    { date: "Oct 15", alerts: 149 },
    { date: "Oct 16", alerts: 238 },
    { date: "Oct 17", alerts: 196 },
    { date: "Oct 18", alerts: 104 },
    { date: "Oct 19", alerts: 91 },
    { date: "Oct 20", alerts: 162 },
    { date: "Oct 21", alerts: 177 },
    { date: "Oct 22", alerts: 158 },
    { date: "Oct 23", alerts: 143 },
  ]
  const config = {
    alerts: { label: "Alerts", color: "var(--chart-1)" },
  } satisfies ChartConfig

  return (
    <ChartContainer config={config} className="aspect-auto h-56 w-full">
      <AreaChart data={data} margin={{ left: 8, right: 8 }}>
        <CartesianGrid vertical={false} />
        <XAxis
          dataKey="date"
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          minTickGap={24}
        />
        <ChartTooltip
          cursor={false}
          content={<ChartTooltipContent indicator="line" />}
        />
        <Area
          dataKey="alerts"
          type="monotone"
          fill="var(--color-alerts)"
          fillOpacity={0.2}
          stroke="var(--color-alerts)"
        />
      </AreaChart>
    </ChartContainer>
  )
}
```

### Donut

A pie chart reads colors from the data: give each row a `fill` that points at its config key,
and pass `nameKey` to the tooltip and legend so they find the labels. A `Label` inside `Pie`
draws the total in the hole. Keep a donut to five slices or fewer.

```tsx
import { Label, Pie, PieChart } from "recharts"
import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@zenoxai/ui/components/chart"

function ChartDonut() {
  const data = [
    { source: "endpoint", alerts: 412, fill: "var(--color-endpoint)" },
    { source: "identity", alerts: 167, fill: "var(--color-identity)" },
    { source: "email", alerts: 89, fill: "var(--color-email)" },
    { source: "cloud", alerts: 64, fill: "var(--color-cloud)" },
    { source: "network", alerts: 38, fill: "var(--color-network)" },
  ]
  const config = {
    alerts: { label: "Alerts" },
    endpoint: { label: "Endpoint", color: "var(--chart-1)" },
    identity: { label: "Identity", color: "var(--chart-2)" },
    email: { label: "Email", color: "var(--chart-3)" },
    cloud: { label: "Cloud", color: "var(--chart-4)" },
    network: { label: "Network", color: "var(--chart-5)" },
  } satisfies ChartConfig
  const total = data.reduce((sum, item) => sum + item.alerts, 0)

  return (
    <ChartContainer config={config} className="aspect-square h-72">
      <PieChart>
        <ChartTooltip
          content={<ChartTooltipContent nameKey="source" hideLabel />}
        />
        <Pie data={data} dataKey="alerts" nameKey="source" innerRadius={60}>
          <Label
            content={({ viewBox }) => {
              if (!viewBox || !("cx" in viewBox)) return null
              const { cx = 0, cy = 0 } = viewBox
              return (
                <text x={cx} y={cy} textAnchor="middle">
                  <tspan
                    x={cx}
                    y={cy}
                    className="fill-foreground text-2xl font-semibold"
                  >
                    {total.toLocaleString("en-US")}
                  </tspan>
                  <tspan x={cx} y={cy + 20} className="fill-text-secondary">
                    Alerts
                  </tspan>
                </text>
              )
            }}
          />
        </Pie>
        <ChartLegend content={<ChartLegendContent nameKey="source" />} />
      </PieChart>
    </ChartContainer>
  )
}
```

## Accessibility

- Recharts 3 turns on its accessibility layer by default: the chart is focusable, and in bar,
  line and area charts the arrow keys move the tooltip between data points.
- A chart is not readable by every user. Put the key figure in text next to it, and offer the
  data as a [Table](/components/table) or an export where the numbers matter.
- Color tells series apart only with a legend or a tooltip. Show the legend whenever there is
  more than one series.

## API reference

`import { ... } from "@zenoxai/ui/components/chart"`

### ChartContainer

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| config (required) | `ChartConfig` |  | Label, icon and color of each data key of the chart. |
| initialDimension | `{ width: number; height: number; }` | `{ width: 320, height: 200 } as const` | Size used until the container measures itself, as on the server. |

### ChartTooltip

No props of its own.

### ChartTooltipContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| hideIndicator | `boolean` | `false` | Hides the color swatch beside each series. |
| hideLabel | `boolean` | `false` | Hides the heading, the label of the hovered point. |
| indicator | `"line" \| "dot" \| "dashed"` | `dot` | Shape of the swatch: `dot` (default), `line` or `dashed`. |
| labelKey | `string` |  | Config key whose label becomes the heading, or the payload field holding it. Defaults to the label of the hovered point. |
| nameKey | `string` |  | Config key that names each series, or the payload field holding it. Defaults to each item's name, then its data key. |

### ChartLegend

No props of its own.

### ChartLegendContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| hideIcon | `boolean` | `false` | Shows the color swatch even where the config sets an icon. |
| nameKey | `string` |  | Config key that names each series, or the payload field holding it. |

### ChartStyle

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| config (required) | `ChartConfig` |  |  |
| id (required) | `string` |  |  |

