
Bubble Chart Generator
- 6 installs
- 2 repo stars
- Updated August 1, 2026
- vishalsachdev/claude-skills
Helps with ai & agent building tasks.
About
bubble-chart-generator is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- bubble-chart-generator
- AI & Agent Building
- AI-coding skill
Bubble Chart Generator by the numbers
- 6 all-time installs (skills.sh)
- +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #12,739 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vishalsachdev/claude-skills --skill bubble-chart-generatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 1, 2026 |
| Repository | vishalsachdev/claude-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Bubble Chart Generator
Overview
This skill generates professional, interactive bubble chart visualizations using Chart.js. Bubble charts are ideal for displaying three-dimensional data on a 2D plane: X-axis, Y-axis, and bubble size. The skill creates a complete MicroSim package suitable for embedding in educational content or documentation sites built with MkDocs.
When to Use This Skill
Use this skill when users request:
- Priority matrices: Impact vs Effort, Risk vs Value, Cost vs Benefit
- Portfolio visualizations: Comparing items across two dimensions with frequency/count as size
- Multi-dimensional data: Any dataset with three key metrics to visualize
- Quadrant analysis: Categorizing items into four strategic zones
- Interactive charts: Need for hover tooltips, clickable elements, or dynamic data display
Common trigger phrases:
- "Create a bubble chart showing..."
- "Visualize the priority matrix for..."
- "Build an interactive scatter plot with..."
- "Show a 2x2 matrix with bubble sizes representing..."
Workflow
Step 1: Understand the Data and Requirements
Before generating the chart, gather information about:
1. Data structure: What items need to be visualized?
- Item names/labels
- X-axis values and label (e.g., "Effort", "Risk")
- Y-axis values and label (e.g., "Impact", "Value")
- Size metric (e.g., "Count", "Cost", "Frequency")
- Optional: Status/category for color coding
2. Context: What is the purpose of the visualization?
- Educational content
- Decision-making tool
- Analysis report
- Portfolio review
3. Integration: Where will the chart be used?
- Standalone page
- Embedded in documentation
- Part of a presentation
- MkDocs site integration
Step 2: Create Directory Structure
Create a new directory for the MicroSim following this pattern:
docs/sims/<chart-name>/
├── main.html # Main visualization file
├── style.css # Styling
└── index.md # Documentation (if part of MkDocs)Naming convention: Use kebab-case (lowercase with hyphens) for directory names that are descriptive and URL-friendly (e.g., skill-impact-chart, portfolio-analysis, risk-assessment-matrix).
Step 3: Create main.html with Chart.js
Generate the main HTML file with the following structure:
1. HTML boilerplate with proper meta tags 2. Chart.js CDN import: https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js 3. Canvas element for the chart 4. Quadrant analysis section (optional but recommended) 5. JavaScript implementation:
- Data array with all items
- Color scheme based on categories/status
- Bubble size calculation function
- Chart.js configuration with bubble chart type
- Custom plugin for quadrant backgrounds and labels
- Quadrant population logic
Key Chart.js configuration elements:
// Data structure
const data = [
{ type: 'item-name', count: 10, impact: 6.7, effort: 1, priority: 6.67, status: 'Category' },
// ... more items
];
// Bubble size scaling
function getBubbleSize(count) {
const minSize = 8;
const maxSize = 30;
return minSize + ((count - minCount) / (maxCount - minCount)) * (maxSize - minSize);
}
// Chart options
{
type: 'bubble',
options: {
aspectRatio: 1.5,
scales: {
x: { min: 0, max: 10, title: { text: 'X-Axis Label' } },
y: { min: 0, max: 10, title: { text: 'Y-Axis Label' } }
},
plugins: {
title: { text: 'Chart Title' },
tooltip: { /* custom tooltip */ }
}
},
plugins: [
// Custom plugin for quadrant backgrounds and labels
]
}Important considerations:
- Prevent edge clipping: Add padding or extend scale ranges (e.g.,
min: -0.5, max: 10.5) - Quadrant shading: Use
afterDrawplugin to add background colors - Label positioning: Position labels above bubbles to avoid overlap
- Responsive design: Set appropriate aspect ratio and container height
Step 4: Create style.css
Generate CSS with professional styling:
1. Reset and base styles: Clean defaults for cross-browser consistency 2. Chart container: Fixed height (typically 600px), padding, box-shadow 3. Legend container: Styling for quadrant analysis section 4. Quadrant sections: Individual styling for each priority zone 5. Interactive elements: Hover effects, transitions 6. Responsive design: Media queries for mobile/tablet 7. Print styles: Print-friendly adjustments
Key design principles:
- Use clean, modern aesthetics
- Provide clear visual hierarchy
- Add subtle shadows and borders
- Use gradients for quadrant labels
- Include hover effects for engagement
- Ensure accessibility with sufficient contrast
Step 5: Create index.md Documentation
If the chart is part of a MkDocs site, create comprehensive documentation:
1. Title and overview: Brief description of the visualization 2. Embedded iframe: Display the chart inline 3 Link to Fullscreen: Markdown link to main.html with 3. Interpretation guide: Explain how to read the chart 4. Features section: List interactive elements 5. Customization guide: Detailed instructions for modifying:
- Chart margins and layout
- Bubble sizes
- Colors (status colors, quadrant backgrounds)
- Data structure
- Label positioning
- Tooltip content
6. Technical details: Dependencies, browser compatibility, file structure 7. Use cases: Other applications for this pattern 8. References: Links to Chart.js docs and related resources
Documentation structure template:
# [Chart Title]
[Brief description]
## Interactive Chart
<iframe src="main.html" width="100%" height="900" frameborder="0"></iframe>
## Overview
[Detailed explanation of what the chart shows]
## Features
### Interactive Elements
- Hover tooltips
- Labeled bubbles
- Quadrant backgrounds
### Visual Design
- Bubble sizes
- Status colors
- Grid lines
## Customization Guide
### Adjusting Chart Margins and Layout
[Specific code examples]
### Adjusting Bubble Sizes
[Specific code examples]
### Changing Colors
[Specific code examples]
## Technical Details
- Dependencies
- Browser compatibility
- File structure
## Use Cases
[Other applications]Step 6: Integrate into Navigation (MkDocs)
If using MkDocs, add the chart to the navigation in mkdocs.yml:
- MicroSims:
- Introduction: sims/index.md
- [Chart Name]: sims/[chart-name]/index.md
- [Other sims...]: ...Place the entry in a logical position based on:
- Related content (group similar visualizations)
- Alphabetical order
- Creation order
Step 7: Test and Validate
Before considering the chart complete:
1. Visual testing:
- Open
main.htmlin a browser directly - Test with
mkdocs serveif applicable - Check all breakpoints (desktop, tablet, mobile)
- Verify no bubbles are clipped at edges
- Confirm labels are readable and positioned correctly
2. Interactive testing:
- Hover over all bubbles to verify tooltips
- Check quadrant list population
- Test on different browsers
3. Documentation review:
- Verify all code examples are accurate
- Test customization instructions
- Check all internal links
4. Data validation:
- Confirm all data points are plotted correctly
- Verify calculations (e.g., priority scores)
- Check category assignments
Best Practices
Data Preparation
1. Normalize scales: Use consistent 0-10 scales for comparability 2. Calculate derived metrics: Include priority scores or ratios 3. Categorize logically: Group items by meaningful status or type 4. Validate completeness: Ensure all required fields are present
Visual Design
1. Color coding: Use intuitive colors (green=good, red=caution) 2. Bubble sizing: Scale proportionally with clear min/max bounds 3. Quadrant shading: Use subtle backgrounds (5-10% opacity) 4. Label clarity: Ensure text is always readable against backgrounds 5. Spacing: Prevent overlap with appropriate margins
Documentation
1. Code examples: Provide exact line numbers and snippets 2. Before/after: Show the effect of customizations 3. Parameter ranges: Suggest appropriate value ranges 4. Common issues: Address edge clipping, overlap, performance
MkDocs Integration
1. Iframe sizing: Use height="900" for full chart visibility 2. Path references: Use relative paths (../sims/...) 3. Navigation placement: Group with related MicroSims 4. Responsive embedding: Add border and border-radius for polish
Common Variations
Simple Scatter Plot (No Bubbles)
For uniform sizing, set all bubbles to the same radius:
function getBubbleSize(count) {
return 10; // Fixed size
}Color by Quadrant
Instead of status, color bubbles by their quadrant position:
function getQuadrantColor(impact, effort) {
if (impact > 5 && effort <= 5) return 'green';
if (impact > 5 && effort > 5) return 'yellow';
if (impact <= 5 && effort <= 5) return 'lightblue';
return 'red';
}Three or More Categories
Expand the color scheme and datasets:
const colors = {
'High': 'rgba(40, 167, 69, 0.8)',
'Medium': 'rgba(255, 193, 7, 0.8)',
'Low': 'rgba(220, 53, 69, 0.8)'
};Time-Series Animation
Add data for multiple time periods and animate transitions (requires additional Chart.js configuration).
Troubleshooting
Bubbles Clipped at Edges
Solution: Extend scale ranges or add layout padding:
scales: {
x: { min: -0.5, max: 10.5 }
}Labels Overlapping
Solution: Increase label offset or reduce font size:
element.y - element.options.radius - 12 // Increase offsetPerformance Issues
Solution: Reduce data points or disable animations:
animation: falseQuadrant Colors Not Showing
Solution: Verify the afterDraw plugin is registered and ctx.globalAlpha is set appropriately (0.05-0.1).
References
This skill uses the following assets and references:
Assets
- Chart.js CDN:
https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js - No local assets required (Chart.js loaded from CDN)
References
Example Implementation
See the skill-impact-chart example at /docs/sims/skill-impact-chart/ for a complete reference implementation.
Bubble Chart Generator Assets
This directory contains template files that can be used as starting points for creating bubble chart visualizations.
Template Files
template-main.html
A complete HTML template with Chart.js bubble chart implementation. Includes:
- HTML boilerplate with proper meta tags
- Chart.js CDN import
- Canvas element for rendering
- Quadrant analysis section
- Fully commented JavaScript with TODO markers for customization
- Data structure example
- Color scheme configuration
- Bubble size calculation
- Chart.js configuration with all options
- Custom plugin for quadrant backgrounds
- Quadrant population logic
Usage: Copy this file to your target directory (e.g., docs/sims/my-chart/main.html) and customize the TODO sections.
template-style.css
Professional CSS styling for bubble chart visualizations. Includes:
- Reset and base styles
- Chart container styling
- Legend and quadrant section styling
- Interactive hover effects
- Responsive design media queries
- Print-friendly styles
Usage: Copy this file to your target directory (e.g., docs/sims/my-chart/style.css) and customize colors/dimensions as needed.
Customization Guide
When using these templates:
1. Replace TODO markers in template-main.html:
- Chart title
- Data array
- Color scheme
- Axis labels
- Quadrant colors
2. Adjust styling in template-style.css:
- Chart container height (default: 600px)
- Color scheme to match your data categories
- Responsive breakpoints if needed
3. Test thoroughly:
- Open main.html directly in browser
- Verify all data points display correctly
- Check tooltips and interactions
- Test on different screen sizes
Example Implementation
For a complete reference implementation, see: /docs/sims/skill-impact-chart/
This demonstrates all features in action with real data.
CHART_NAME
TODO - put in the name of the chart and fill in the explanation of the chart and how it can be customized
<iframe src="main" height="400px" width="100%" scrolling="no"><iframe> View the Chart Fullscreen
Explanation of Chart
Customizing the Chart Appearance
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[Chart Title] - Priority Matrix</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
</head>
<body>
<div class="chart-container">
<canvas id="priorityMatrix"></canvas>
</div>
<div class="legend-container">
<h3>Quadrant Analysis</h3>
<div class="quadrant">
<div class="quadrant-label high-impact-low-effort">High Impact, Low Effort (Priority 1)</div>
<ul id="q1-items"></ul>
</div>
<div class="quadrant">
<div class="quadrant-label high-impact-high-effort">High Impact, High Effort (Priority 2)</div>
<ul id="q2-items"></ul>
</div>
<div class="quadrant">
<div class="quadrant-label low-impact-low-effort">Low Impact, Low Effort (Priority 3)</div>
<ul id="q3-items"></ul>
</div>
<div class="quadrant">
<div class="quadrant-label low-impact-high-effort">Low Impact, High Effort (Priority 4)</div>
<ul id="q4-items"></ul>
</div>
</div>
<script>
// TODO: Replace with actual data
const data = [
{ type: 'item-1', count: 10, xValue: 6.7, yValue: 1, status: 'Category A' },
{ type: 'item-2', count: 5, xValue: 3.3, yValue: 0, status: 'Category B' },
// Add more items...
];
// TODO: Customize colors for your categories
const colors = {
'Category A': 'rgba(220, 53, 69, 0.8)',
'Category B': 'rgba(40, 167, 69, 0.8)'
};
const borderColors = {
'Category A': 'rgb(220, 53, 69)',
'Category B': 'rgb(40, 167, 69)'
};
// Calculate bubble sizes
const minCount = Math.min(...data.map(d => d.count));
const maxCount = Math.max(...data.map(d => d.count));
function getBubbleSize(count) {
const minSize = 8;
const maxSize = 30;
return minSize + ((count - minCount) / (maxCount - minCount)) * (maxSize - minSize);
}
// Prepare scatter plot data
const scatterData = data.map(item => ({
x: item.xValue,
y: item.yValue,
r: getBubbleSize(item.count),
label: item.type,
count: item.count,
status: item.status
}));
// Group by status/category
const uniqueStatuses = [...new Set(data.map(d => d.status))];
const datasets = uniqueStatuses.map(status => ({
label: status,
data: scatterData.filter(d => d.status === status),
backgroundColor: colors[status],
borderColor: borderColors[status],
borderWidth: 2
}));
// Create chart
const ctx = document.getElementById('priorityMatrix').getContext('2d');
const chart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: true,
aspectRatio: 1.5,
plugins: {
title: {
display: true,
text: '[Chart Title]', // TODO: Customize title
font: {
size: 18,
weight: 'bold'
},
padding: 20
},
legend: {
display: true,
position: 'top',
labels: {
font: {
size: 12
},
usePointStyle: true
}
},
tooltip: {
callbacks: {
label: function(context) {
const item = context.raw;
return [
`Type: ${item.label}`,
`Count: ${item.count}`,
`Y-Value: ${item.y}`,
`X-Value: ${item.x}`,
`Status: ${item.status}`
];
}
}
}
},
scales: {
x: {
title: {
display: true,
text: 'X-Axis Label →', // TODO: Customize label
font: {
size: 14,
weight: 'bold'
}
},
min: 0,
max: 10,
ticks: {
stepSize: 1
},
grid: {
color: 'rgba(200, 200, 200, 0.2)'
}
},
y: {
title: {
display: true,
text: 'Y-Axis Label →', // TODO: Customize label
font: {
size: 14,
weight: 'bold'
}
},
min: 0,
max: 10,
ticks: {
stepSize: 1
},
grid: {
color: 'rgba(200, 200, 200, 0.2)'
}
}
}
},
plugins: [{
afterDraw: function(chart) {
const ctx = chart.ctx;
const chartArea = chart.chartArea;
const xMid = (chartArea.left + chartArea.right) / 2;
const yMid = (chartArea.top + chartArea.bottom) / 2;
// Draw quadrant backgrounds
ctx.save();
ctx.globalAlpha = 0.05;
// TODO: Customize quadrant colors
ctx.fillStyle = 'green'; // High Y, Low X
ctx.fillRect(chartArea.left, chartArea.top, xMid - chartArea.left, yMid - chartArea.top);
ctx.fillStyle = 'yellow'; // High Y, High X
ctx.fillRect(xMid, chartArea.top, chartArea.right - xMid, yMid - chartArea.top);
ctx.fillStyle = 'lightblue'; // Low Y, Low X
ctx.fillRect(chartArea.left, yMid, xMid - chartArea.left, chartArea.bottom - yMid);
ctx.fillStyle = 'red'; // Low Y, High X
ctx.fillRect(xMid, yMid, chartArea.right - xMid, chartArea.bottom - yMid);
ctx.restore();
// Add labels to each point
chart.data.datasets.forEach((dataset, datasetIndex) => {
const meta = chart.getDatasetMeta(datasetIndex);
meta.data.forEach((element, index) => {
const dataPoint = dataset.data[index];
ctx.fillStyle = '#333';
ctx.font = 'bold 10px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(
`${dataPoint.label} (n=${dataPoint.count})`,
element.x,
element.y - element.options.radius - 8
);
});
});
}
}]
});
// Populate quadrant analysis lists
function populateQuadrants() {
const q1 = document.getElementById('q1-items'); // High Y, Low X
const q2 = document.getElementById('q2-items'); // High Y, High X
const q3 = document.getElementById('q3-items'); // Low Y, Low X
const q4 = document.getElementById('q4-items'); // Low Y, High X
data.forEach(item => {
const li = document.createElement('li');
li.innerHTML = `<strong>${item.type}</strong> (Y: ${item.yValue}, X: ${item.xValue}, Count: ${item.count})`;
// Determine quadrant (using 5 as midpoint)
if (item.yValue > 5 && item.xValue <= 5) {
q1.appendChild(li);
} else if (item.yValue > 5 && item.xValue > 5) {
q2.appendChild(li);
} else if (item.yValue <= 5 && item.xValue <= 5) {
q3.appendChild(li);
} else {
q4.appendChild(li);
}
});
// Add "None" message if quadrant is empty
[q1, q2, q3, q4].forEach(ul => {
if (ul.children.length === 0) {
const li = document.createElement('li');
li.textContent = 'None';
li.style.color = '#888';
ul.appendChild(li);
}
});
}
populateQuadrants();
</script>
</body>
</html>
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #ffffff;
padding: 20px;
color: #333;
max-width: 1200px;
margin: 0 auto;
}
/* Chart container */
.chart-container {
position: relative;
width: 100%;
height: 600px;
margin-bottom: 30px;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* Legend container */
.legend-container {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.legend-container h3 {
margin-bottom: 20px;
color: #2c3e50;
font-size: 1.4em;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
}
/* Quadrant sections */
.quadrant {
margin-bottom: 20px;
background: white;
border-radius: 6px;
padding: 15px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
.quadrant-label {
font-weight: bold;
font-size: 1.1em;
margin-bottom: 10px;
padding: 8px 12px;
border-radius: 4px;
color: white;
}
.high-impact-low-effort {
background: linear-gradient(135deg, #27ae60, #2ecc71);
}
.high-impact-high-effort {
background: linear-gradient(135deg, #f39c12, #f1c40f);
}
.low-impact-low-effort {
background: linear-gradient(135deg, #3498db, #5dade2);
}
.low-impact-high-effort {
background: linear-gradient(135deg, #e74c3c, #c0392b);
}
/* List styling */
.quadrant ul {
list-style: none;
padding-left: 0;
}
.quadrant li {
padding: 8px 12px;
margin: 5px 0;
background: #f8f9fa;
border-left: 4px solid #3498db;
border-radius: 3px;
font-size: 0.95em;
transition: all 0.2s ease;
}
.quadrant li:hover {
background: #e8f4f8;
transform: translateX(5px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
.quadrant li strong {
color: #2c3e50;
font-size: 1.05em;
}
/* Responsive design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.chart-container {
height: 400px;
padding: 10px;
}
.legend-container {
padding: 15px;
}
.legend-container h3 {
font-size: 1.2em;
}
.quadrant-label {
font-size: 1em;
}
.quadrant li {
font-size: 0.9em;
}
}
/* Print styles */
@media print {
.chart-container {
box-shadow: none;
page-break-inside: avoid;
}
.legend-container {
box-shadow: none;
}
.quadrant {
page-break-inside: avoid;
}
}