> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Donut

> Donut chart implementation for @servicetitan/anvil2-charts-kit.

A donut chart shows part-to-whole relationships. Each slice is a variable, so `variant` assigns color per datum. Use `monochrome` for four or fewer slices, and `categorical` for five or more, grouping the remainder into an "Other" slice. Donut charts do not use the semantic palette.

* Design rules: [Donut charts](https://anvil.servicetitan.com/docs/data-visualization/donut-charts.md)
* Shared API / selection / tooltips / palettes: [Chart component](https://anvil.servicetitan.com/docs/charts/web/chart.md)

***

## Minimal option

A donut is a pie series with an inner radius. The `padAngle` and slice corner radius come from `variant`.

charts-react-donut

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/charts-react-donut?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Chart, type EChartsOption } from "@servicetitan/anvil2-charts-kit";
import "@servicetitan/anvil2-charts-kit/styles.css";

function App() {
  const option: EChartsOption = {
    legend: { orient: "vertical", right: 8, top: "middle" },
    series: [
      {
        type: "pie",
        radius: ["40%", "70%"],
        data: [
          { name: "Organic", value: 48 },
          { name: "Paid", value: 32 },
          { name: "Referral", value: 14 },
          { name: "Other", value: 6 },
        ],
      },
    ],
  };

  return (
    <Chart
      option={option}
      variant="monochrome"
      width={480}
      height={300}
      accessibilityLabel="Donut chart. Revenue by channel."
    />
  );
}

export default App;
```

***

## Variant styling

`variant` applies the following treatment:

| Concern | Behavior |
| - | - |
| Color | Monochrome steps or categorical hues by slice index |
| Gaps | `padAngle` of roughly 4px between slices |
| Slice corners | `borderRadius` on slices |
| Hover | Series focus and blur |

Avoid hand-authoring slice gaps or palette hex values to reproduce this treatment.

***

## Labels inside slices

Direct labels follow the same pattern as bar end labels. The chart theme styles `pie.label` with a font, a translucent background, and padding, and leaves it at `show: false`. Enable it with `position: "inside"`.

Use percent of total for part-to-whole, which ECharts writes as `{d}%`. Raw values through `{c}` also work. Categorical donut charts require direct labels, because the slice colors do not all reach a 3:1 contrast ratio against each other. Leave `renderTooltip` unset when the in-slice labels carry enough information. Small slices clip their labels, so group them into an "Other" slice.

charts-react-donut-direct-labels

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/charts-react-donut-direct-labels?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Chart, type EChartsOption } from "@servicetitan/anvil2-charts-kit";
import "@servicetitan/anvil2-charts-kit/styles.css";

function App() {
  const option: EChartsOption = {
    legend: { orient: "vertical", right: 8, top: "middle" },
    series: [
      {
        type: "pie",
        radius: ["40%", "70%"],
        data: [
          { name: "Organic", value: 48 },
          { name: "Paid", value: 32 },
          { name: "Referral", value: 14 },
          { name: "Other", value: 6 },
        ],
        label: {
          show: true,
          position: "inside",
          formatter: "{d}%",
        },
      },
    ],
  };

  return (
    <Chart
      option={option}
      variant="monochrome"
      width={480}
      height={300}
      accessibilityLabel="Donut chart. Revenue by channel."
    />
  );
}

export default App;
```

***

## Labels outside with a leader line

Outside labels suit charts where in-slice text would clutter or clip. Set `position: "outside"` and `labelLine: { show: true }`. Add the category to the formatter through `{b}` when the chart has no legend, and leave room in the plot by reducing `radius` or dropping the side legend so the labels do not collide.

charts-react-donut-outside-labels

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/charts-react-donut-outside-labels?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Chart, type EChartsOption } from "@servicetitan/anvil2-charts-kit";
import "@servicetitan/anvil2-charts-kit/styles.css";

function App() {
  const option: EChartsOption = {
    series: [
      {
        type: "pie",
        radius: ["35%", "55%"],
        data: [
          { name: "Organic", value: 48 },
          { name: "Paid", value: 32 },
          { name: "Referral", value: 14 },
          { name: "Other", value: 6 },
        ],
        label: {
          show: true,
          position: "outside",
          formatter: "{b}\n{d}%",
        },
        labelLine: { show: true },
      },
    ],
  };

  return (
    <Chart
      option={option}
      variant="monochrome"
      width={480}
      height={300}
      accessibilityLabel="Donut chart. Revenue by channel."
    />
  );
}

export default App;
```

***

## Mixed labels by slice size

The series defaults to inside labels, and slices below a percentage threshold override to outside labels with a `labelLine`. ECharts merges per-slice `label` and `labelLine` values onto the series defaults. The threshold is a product decision: the 20% cutoff in this example is not a design system constant.

charts-react-donut-mixed-labels

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/charts-react-donut-mixed-labels?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Chart, type EChartsOption } from "@servicetitan/anvil2-charts-kit";
import "@servicetitan/anvil2-charts-kit/styles.css";

function App() {
  const data = [
    { name: "Organic", value: 48 },
    { name: "Paid", value: 32 },
    { name: "Referral", value: 14 },
    { name: "Other", value: 6 },
  ];
  const MIN_PERCENT = 20;
  const total = data.reduce((sum, d) => sum + d.value, 0);
  const slices = data.map((d) => {
    const pct = (d.value / total) * 100;
    if (pct >= MIN_PERCENT) return d;
    return {
      ...d,
      label: { position: "outside" as const, formatter: "{b}\n{d}%" },
      labelLine: { show: true },
    };
  });

  const option: EChartsOption = {
    series: [
      {
        type: "pie",
        radius: ["35%", "55%"],
        data: slices,
        label: { show: true, position: "inside", formatter: "{d}%" },
        labelLine: { show: false },
      },
    ],
  };

  return (
    <Chart
      option={option}
      variant="monochrome"
      width={480}
      height={300}
      accessibilityLabel="Donut chart. Revenue by channel."
    />
  );
}

export default App;
```

***

## Tooltips

Pass `renderTooltip` for richer hover content. See [Chart component](https://anvil.servicetitan.com/docs/charts/web/chart.md#tooltips).

charts-react-donut-tooltip

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/charts-react-donut-tooltip?file=src/App.tsx)

```tsx lines expandable theme={null}
import {
  Chart,
  type ChartTooltipInfo,
  type EChartsOption,
} from "@servicetitan/anvil2-charts-kit";
import "@servicetitan/anvil2-charts-kit/styles.css";
import { CartoTheme, Text, TooltipSurface } from "@servicetitan/anvil2-ai-kit";
// This file is most likely already in your workspace
import "@servicetitan/anvil2-ai-kit/styles.css";

function renderTooltip({ name, points }: ChartTooltipInfo) {
  return (
    <TooltipSurface
      content={
        <>
          <Text text={name} />
          {points.map((point) => (
            <Text
              key={point.seriesIndex}
              text={`${point.seriesName ? `${point.seriesName}: ` : ""}${String(point.value)}`}
            />
          ))}
        </>
      }
    />
  );
}

function App() {
  const option: EChartsOption = {
    legend: { orient: "vertical", right: 8, top: "middle" },
    series: [
      {
        type: "pie",
        radius: ["40%", "70%"],
        data: [
          { name: "Organic", value: 48 },
          { name: "Paid", value: 32 },
          { name: "Referral", value: 14 },
          { name: "Other", value: 6 },
        ],
      },
    ],
  };

  return (
    <CartoTheme>
      <Chart
        option={option}
        variant="monochrome"
        renderTooltip={renderTooltip}
        width={480}
        height={300}
        accessibilityLabel="Donut chart. Revenue by channel."
      />
    </CartoTheme>
  );
}

export default App;
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.