From bbeierle12-skill-mcp-claude
Create D3.js charts and interactive data visualizations. Use when building bar charts, line charts, scatter plots, pie charts, force-directed graphs, geographic maps, or any custom data visualization.
npx claudepluginhub joshuarweaver/cascade-code-languages-misc-1 --plugin bbeierle12-skill-mcp-claudeThis skill uses the workspace's default tool permissions.
```javascript
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
// Select elements
const svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height);
// Bind data to elements
svg.selectAll('rect')
.data(data)
.join('rect')
.attr('x', (d, i) => i * barWidth)
.attr('y', d => height - scale(d.value))
.attr('width', barWidth - 1)
.attr('height', d => scale(d.value));
// Linear scale (continuous → continuous)
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, width]);
// Band scale (discrete → continuous)
const xScale = d3.scaleBand()
.domain(data.map(d => d.name))
.range([0, width])
.padding(0.1);
// Time scale
const xScale = d3.scaleTime()
.domain([startDate, endDate])
.range([0, width]);
// Color scale
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
// Create axes
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
// Append to SVG
svg.append('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
svg.append('g')
.attr('class', 'y-axis')
.call(yAxis);
function createBarChart(data, container, options = {}) {
const {
width = 600,
height = 400,
margin = { top: 20, right: 20, bottom: 30, left: 40 }
} = options;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const svg = d3.select(container)
.append('svg')
.attr('width', width)
.attr('height', height);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const xScale = d3.scaleBand()
.domain(data.map(d => d.name))
.range([0, innerWidth])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.nice()
.range([innerHeight, 0]);
// Bars
g.selectAll('.bar')
.data(data)
.join('rect')
.attr('class', 'bar')
.attr('x', d => xScale(d.name))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => innerHeight - yScale(d.value))
.attr('fill', 'steelblue');
// Axes
g.append('g')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale));
g.append('g')
.call(d3.axisLeft(yScale));
return svg.node();
}
function createLineChart(data, container, options = {}) {
const {
width = 600,
height = 400,
margin = { top: 20, right: 20, bottom: 30, left: 40 }
} = options;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const svg = d3.select(container)
.append('svg')
.attr('width', width)
.attr('height', height);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, innerWidth]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.nice()
.range([innerHeight, 0]);
// Line generator
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value))
.curve(d3.curveMonotoneX);
// Path
g.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 2)
.attr('d', line);
// Dots
g.selectAll('.dot')
.data(data)
.join('circle')
.attr('class', 'dot')
.attr('cx', d => xScale(d.date))
.attr('cy', d => yScale(d.value))
.attr('r', 4)
.attr('fill', 'steelblue');
// Axes
g.append('g')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale));
g.append('g')
.call(d3.axisLeft(yScale));
return svg.node();
}
function createPieChart(data, container, options = {}) {
const {
width = 400,
height = 400,
innerRadius = 0, // 0 for pie, > 0 for donut
} = options;
const radius = Math.min(width, height) / 2;
const svg = d3.select(container)
.append('svg')
.attr('width', width)
.attr('height', height);
const g = svg.append('g')
.attr('transform', `translate(${width / 2},${height / 2})`);
const color = d3.scaleOrdinal(d3.schemeCategory10);
const pie = d3.pie()
.value(d => d.value)
.sort(null);
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(radius - 10);
const arcs = g.selectAll('.arc')
.data(pie(data))
.join('g')
.attr('class', 'arc');
arcs.append('path')
.attr('d', arc)
.attr('fill', d => color(d.data.name));
arcs.append('text')
.attr('transform', d => `translate(${arc.centroid(d)})`)
.attr('text-anchor', 'middle')
.text(d => d.data.name);
return svg.node();
}
// Create tooltip
const tooltip = d3.select('body')
.append('div')
.attr('class', 'tooltip')
.style('position', 'absolute')
.style('visibility', 'hidden')
.style('background', 'white')
.style('padding', '10px')
.style('border-radius', '4px')
.style('box-shadow', '0 2px 4px rgba(0,0,0,0.2)');
// Add to elements
bars.on('mouseover', function(event, d) {
tooltip
.style('visibility', 'visible')
.html(`<strong>${d.name}</strong><br/>Value: ${d.value}`);
})
.on('mousemove', function(event) {
tooltip
.style('top', (event.pageY - 10) + 'px')
.style('left', (event.pageX + 10) + 'px');
})
.on('mouseout', function() {
tooltip.style('visibility', 'hidden');
});
// Animate on data update
bars.transition()
.duration(750)
.attr('y', d => yScale(d.value))
.attr('height', d => innerHeight - yScale(d.value));
// Staggered animation
bars.transition()
.delay((d, i) => i * 50)
.duration(500)
.attr('opacity', 1);
const zoom = d3.zoom()
.scaleExtent([1, 8])
.on('zoom', (event) => {
g.attr('transform', event.transform);
});
svg.call(zoom);
join() instead of enter/update/exit for cleaner codearia-label to SVGrole="img" for decorative charts// Parse dates
const parseDate = d3.timeParse('%Y-%m-%d');
data.forEach(d => {
d.date = parseDate(d.dateString);
});
// Format numbers
const formatNumber = d3.format(',.0f');
const formatCurrency = d3.format('$,.2f');
const formatPercent = d3.format('.1%');