home / skills / dennisadriaans / vue-chrts / donut-chart

This skill helps you build and customize DonutChart components in Vue 3 to visualize part-to-whole data with colors and legends.

npx playbooks add skill dennisadriaans/vue-chrts --skill donut-chart

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.4 KB
---
name: donut-chart
description: Build DonutChart components. Use for part-to-whole relationships and percentage breakdowns.
---

# DonutChart

DonutCharts show proportional data as segments of a circle. Supports full donut and half-donut (gauge) styles.

## Mental Model

```
┌─────────────────────────────────────────────────────────────┐
│  DONUT DATA IS DIFFERENT!                                   │
│                                                             │
│  Other Charts:              DonutChart:                     │
│  data = [                   data = [30, 70, 100]  ← numbers │
│    { label, value },        categories = {                  │
│    { label, value }           a: { name, color },           │
│  ]                            b: { name, color },           │
│                               c: { name, color }            │
│                             }                               │
│                                                             │
│  Categories keys match data index order (a=30, b=70, c=100) │
└─────────────────────────────────────────────────────────────┘
```

## Complete Example

```vue
<script setup lang="ts">
import { DonutChart, DonutType, LegendPosition } from 'vue-chrts'

// Data is just an array of numbers!
const data = [35, 25, 20, 15, 5]

// Categories map to data indices in order
const categories = {
  organic: { name: 'Organic Search', color: '#3b82f6' },
  direct: { name: 'Direct', color: '#10b981' },
  referral: { name: 'Referral', color: '#f59e0b' },
  social: { name: 'Social', color: '#8b5cf6' },
  other: { name: 'Other', color: '#6b7280' }
}
</script>

<template>
  <DonutChart
    :data="data"
    :categories="categories"
    :radius="120"
    :arcWidth="40"
    :legendPosition="LegendPosition.BottomCenter"
    :padAngle="0.02"
  />
</template>
```

## Half Donut (Gauge Style)

```vue
<DonutChart
  :data="[75, 25]"
  :categories="{
    complete: { name: 'Complete', color: '#10b981' },
    remaining: { name: 'Remaining', color: '#e5e7eb' }
  }"
  :radius="100"
  :arcWidth="30"
  :type="DonutType.Half"
/>
```

## Key Props Reference

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `data` | `number[]` | required | **Array of numeric values** |
| `categories` | `Record<string, BulletLegendItem>` | required | Labels/colors for each segment |
| `radius` | `number` | required | Outer radius in pixels |
| `arcWidth` | `number` | - | Width of the donut ring |
| `height` | `number` | - | Container height (optional) |
| `type` | `DonutType` | `Full` | `Full` or `Half` donut |
| `padAngle` | `number` | `0` | Gap between segments (radians) |
| `hideLegend` | `boolean` | `false` | Hide the legend |
| `hideTooltip` | `boolean` | `false` | Hide tooltips |
| `legendPosition` | `LegendPosition` | - | Legend placement |
| `legendStyle` | `string \| Record<string, string>` | - | Custom legend styles |
| `tooltipTitleFormatter` | `(data: T) => string` | - | Custom tooltip title |
| `duration` | `number` | - | Animation duration in ms |

## Sizing Guide

```
┌─────────────────────────────────────────────────────────────┐
│  DONUT SIZING                                               │
│                                                             │
│  radius = outer edge distance from center                   │
│  arcWidth = thickness of the donut ring                     │
│                                                             │
│        ┌───────────┐                                        │
│       ╱             ╲       radius = 100                    │
│      │   ┌─────┐     │      arcWidth = 30                   │
│      │   │     │     │                                      │
│      │   │ 70px│     │      inner radius = 100 - 30 = 70    │
│      │   │     │     │                                      │
│      │   └─────┘     │                                      │
│       ╲             ╱                                       │
│        └───────────┘                                        │
│            100px                                            │
└─────────────────────────────────────────────────────────────┘
```

## Common Patterns

### Progress Indicator

```vue
<script setup>
const progress = 73
const data = [progress, 100 - progress]

const categories = {
  complete: { name: `${progress}% Complete`, color: '#10b981' },
  remaining: { name: 'Remaining', color: '#e5e7eb' }
}
</script>

<template>
  <DonutChart
    :data="data"
    :categories="categories"
    :radius="80"
    :arcWidth="16"
    :type="DonutType.Half"
    :hideLegend="true"
  />
</template>
```

### Budget Breakdown

```vue
<script setup>
const budgetData = [
  { category: 'Housing', amount: 2000, color: '#3b82f6' },
  { category: 'Food', amount: 600, color: '#10b981' },
  { category: 'Transport', amount: 400, color: '#f59e0b' },
  { category: 'Utilities', amount: 300, color: '#8b5cf6' },
  { category: 'Other', amount: 700, color: '#6b7280' },
]

// Transform to DonutChart format
const data = budgetData.map(item => item.amount)
const categories = Object.fromEntries(
  budgetData.map((item, index) => [
    `cat${index}`,
    { name: item.category, color: item.color }
  ])
)
</script>

<template>
  <DonutChart
    :data="data"
    :categories="categories"
    :radius="120"
    :arcWidth="50"
    :legendPosition="LegendPosition.BottomCenter"
  />
</template>
```

## Gotchas

1. **Data must be numbers array**: Unlike other charts, DonutChart takes `number[]` not `object[]`
2. **Categories order matters**: First category = first number in data array
3. **Category count must match**: Number of categories must equal data array length
4. **No xFormatter/yFormatter**: Donut charts don't have axes
5. **Height is optional**: Chart sizes based on radius; height just sets container

Overview

This skill builds DonutChart Vue components for visualizing part-to-whole relationships and percentage breakdowns. It supports full donuts and half-donut (gauge) styles, customizable radii, arc widths, colors, legends, and simple animation options. Use it to display progress, budget splits, and other proportional data with clear labels and tooltips.

How this skill works

DonutChart expects a numeric array for data and a categories map whose keys correspond to data indices in order. It renders SVG arcs sized by value, using category colors and names for legends and tooltips. Props control layout (radius, arcWidth, type), spacing (padAngle), visibility (hideLegend, hideTooltip), and presentation (legendPosition, legendStyle, tooltipTitleFormatter).

When to use it

  • Showing relative proportions or percentage breakdowns (market share, budget, survey results)
  • Displaying a single-value progress indicator using a half-donut/gauge
  • Replacing stacked bars or pie charts when you want a ring-style visual
  • When you need a compact legend and color-coded segments for simple comparisons

Best practices

  • Provide data as a plain number[]; transform objects to numbers before passing to the component
  • Ensure the categories map keys are ordered to match the data array index order
  • Keep category count equal to data length to avoid mismatches
  • Choose radius and arcWidth together so inner radius remains readable (inner radius = radius - arcWidth)
  • Use padAngle sparingly to create visual separation without breaking small segments

Example use cases

  • Progress indicator: render a half-donut for completion percentage with two segments (complete, remaining)
  • Budget breakdown: map category amounts to a number[] and supply a categories map with names and colors
  • Traffic sources: show relative traffic by channel using a compact legend and color-coded segments
  • Simple KPI dashboard: present several DonutCharts side-by-side for quick proportion comparisons

FAQ

What format must data be in?

Data must be an array of numbers (number[]). DonutChart does not accept object arrays like other chart types.

How do categories map to data?

Categories are a keyed object whose entries map to data indices in order. The first category corresponds to data[0], the second to data[1], and so on.

Can I create a gauge-style donut?

Yes. Set the type prop to DonutType.Half and provide two segments (e.g., complete and remaining) to create a gauge-style display.