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

bubble-chart skill

/.claude/skills/bubble-chart

This skill helps you build BubbleChart components in Vue to visualize three variables using x, y, and size.

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

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

Files (1)
SKILL.md
6.7 KB
---
name: bubble-chart
description: Build BubbleChart components. Use for scatter plots with size encoding, showing relationships between three variables.
---

# BubbleChart

BubbleChart displays data points as circles where position (x, y) and size encode three different variables. Great for showing relationships and clusters.

## Mental Model

```
┌─────────────────────────────────────────────────────────────┐
│  THREE DIMENSIONS OF DATA                                   │
│                                                             │
│  Y-Axis                                                     │
│    ▲       ◯ large                                         │
│    │    ●       (size = third variable)                    │
│    │         ◉                                             │
│    │  ●              ◯                                     │
│    │       ◉                                               │
│    │  small ●                                              │
│    └──────────────────────► X-Axis                         │
│                                                             │
│  • xAccessor: horizontal position                          │
│  • yAccessor: vertical position                            │
│  • sizeAccessor: bubble size                               │
│  • categoryKey: bubble color                               │
└─────────────────────────────────────────────────────────────┘
```

## Complete Example

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

interface CompanyData {
  name: string
  revenue: number      // x-axis
  growth: number       // y-axis
  employees: number    // bubble size
  sector: string       // color category
}

const data: CompanyData[] = [
  { name: 'TechCorp', revenue: 500, growth: 25, employees: 1200, sector: 'tech' },
  { name: 'FinanceInc', revenue: 800, growth: 12, employees: 3500, sector: 'finance' },
  { name: 'HealthPlus', revenue: 300, growth: 35, employees: 800, sector: 'health' },
  { name: 'RetailMax', revenue: 1200, growth: 8, employees: 5000, sector: 'retail' },
  { name: 'GreenEnergy', revenue: 200, growth: 45, employees: 400, sector: 'tech' },
  { name: 'BankFirst', revenue: 950, growth: 6, employees: 4200, sector: 'finance' },
]

// Categories define colors for each unique sector value
const categories = {
  tech: { name: 'Technology', color: '#3b82f6' },
  finance: { name: 'Finance', color: '#10b981' },
  health: { name: 'Healthcare', color: '#f59e0b' },
  retail: { name: 'Retail', color: '#8b5cf6' }
}

const xFormatter = (tick: number) => `$${tick}M`
const yFormatter = (tick: number) => `${tick}%`
</script>

<template>
  <BubbleChart
    :data="data"
    :categories="categories"
    categoryKey="sector"
    :xAccessor="(d) => d.revenue"
    :yAccessor="(d) => d.growth"
    :sizeAccessor="(d) => d.employees"
    :sizeRange="[10, 50]"
    :height="400"
    :xFormatter="xFormatter"
    :yFormatter="yFormatter"
    xLabel="Revenue (Millions)"
    yLabel="Growth Rate (%)"
    :legendPosition="LegendPosition.TopRight"
    :opacity="0.7"
  />
</template>
```

## Key Props Reference

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `data` | `T[]` | required | Array of data objects |
| `categories` | `Record<string, BulletLegendItem>` | required | Color mapping for categoryKey values |
| `categoryKey` | `keyof T` | required | Data key for bubble coloring |
| `xAccessor` | `(d: T) => number` | - | Function to get x-position |
| `yAccessor` | `(d: T) => number` | - | Function to get y-position |
| `sizeAccessor` | `(d: T) => number` | - | Function to get bubble size |
| `sizeRange` | `[number, number]` | `[1, 20]` | Min/max bubble radius in pixels |
| `opacity` | `number` | - | Bubble opacity (0-1) |
| `height` | `number` | required | Chart height in pixels |
| `xFormatter` | `(tick, i?, ticks?) => string` | - | X-axis label formatter |
| `yFormatter` | `(tick, i?, ticks?) => string` | - | Y-axis label formatter |
| `labelPosition` | `Position` | `Bottom` | Position of bubble labels |
| `xLabel` | `string` | - | X-axis title |
| `yLabel` | `string` | - | Y-axis title |
| `hideLegend` | `boolean` | `false` | Hide the legend |
| `hideTooltip` | `boolean` | `false` | Hide tooltips |
| `xGridLine` | `boolean` | `false` | Show vertical grid lines |
| `yGridLine` | `boolean` | `false` | Show horizontal grid lines |

## Common Patterns

### Simple Scatter Plot (No Size Variation)

```vue
<BubbleChart
  :data="data"
  :categories="categories"
  categoryKey="type"
  :xAccessor="(d) => d.x"
  :yAccessor="(d) => d.y"
  :sizeRange="[8, 8]"
  :height="300"
/>
```

### Performance Matrix

```vue
<script setup>
const teamData = [
  { team: 'Alpha', velocity: 45, quality: 92, size: 8, status: 'active' },
  { team: 'Beta', velocity: 38, quality: 88, size: 12, status: 'active' },
  { team: 'Gamma', velocity: 52, quality: 78, size: 6, status: 'new' },
]

const categories = {
  active: { name: 'Active Teams', color: '#10b981' },
  new: { name: 'New Teams', color: '#f59e0b' }
}
</script>

<template>
  <BubbleChart
    :data="teamData"
    :categories="categories"
    categoryKey="status"
    :xAccessor="(d) => d.velocity"
    :yAccessor="(d) => d.quality"
    :sizeAccessor="(d) => d.size"
    :sizeRange="[15, 45]"
    :height="350"
    xLabel="Velocity (story points/sprint)"
    yLabel="Quality Score (%)"
    :xGridLine="true"
    :yGridLine="true"
  />
</template>
```

## Accessors Explained

```typescript
// Accessors are functions that extract values from each data item

const data = [
  { name: 'A', sales: 100, profit: 20, marketShare: 15 }
]

// Each accessor takes a data item and returns a number
const xAccessor = (d) => d.sales       // position on x-axis
const yAccessor = (d) => d.profit      // position on y-axis  
const sizeAccessor = (d) => d.marketShare  // bubble size
```

## Gotchas

1. **categoryKey is required**: Unlike other charts, you must specify which data key determines bubble color
2. **Categories must cover all values**: Every unique value in categoryKey must have a category entry
3. **sizeRange affects perception**: Use reasonable ranges; too large bubbles overlap, too small are invisible
4. **Accessors return numbers**: Make sure accessor functions return numeric values, not strings
5. **Opacity helps overlapping**: Use `opacity` prop when bubbles might overlap

Overview

This skill builds BubbleChart components for Vue 3 to visualize three-dimensional relationships by encoding x, y and size into circle position and radius. It is designed for scatter plots where a categorical key maps to bubble color and size encodes a third numeric variable. Use it to reveal clusters, outliers, and correlations in compact, interactive charts.

How this skill works

The component accepts an array of data objects plus three accessor functions: xAccessor, yAccessor and sizeAccessor to extract numeric values for position and radius. A required categoryKey links each data item to a color defined in a categories map. Additional props control sizeRange, axis formatters, labels, grid lines, opacity, legend and tooltips.

When to use it

  • Compare three variables per item (two positional, one size) to surface correlations and trade-offs
  • Highlight clusters and outliers across categories with color encoding
  • Show performance matrices or market maps where size represents importance or volume
  • When you need interactive legends and formatted axis labels for presentation-grade charts
  • When compact visual summaries must show category-level differences at a glance

Best practices

  • Always provide categoryKey and a complete categories mapping for every category value
  • Normalize size values or choose an appropriate sizeRange to avoid overplotting or invisible bubbles
  • Use opacity (0.4–0.8) when bubbles overlap to preserve visibility
  • Provide xFormatter and yFormatter to improve axis readability (currency, percentages, units)
  • Label axes (xLabel, yLabel) and add grid lines when precise comparisons are required

Example use cases

  • Company performance map: revenue (x), growth (%) (y), employees (size), sector (color) for investor presentations
  • Team performance matrix: velocity (x), quality (y), team size (size), status (color) to prioritize coaching
  • Market analysis: product sales (x), profitability (y), market share (size), category (color) to spot opportunities
  • Customer segmentation: recency (x), frequency (y), monetary value (size), cohort (color) to target campaigns
  • Risk assessment: likelihood (x), impact (y), exposure (size), risk type (color) for dashboards

FAQ

What happens if a category value is missing from categories?

Every unique categoryKey value must have an entry in categories. Missing values will not render with a defined color and can break the legend or visual grouping.

Can I use a constant bubble size for a simple scatter?

Yes. Set sizeAccessor accordingly or set sizeRange to the same value (e.g., [8,8]) to make all bubbles equal size.