mcpField
D
d3js-visualization

d3js-visualization

github.com/bbeierle12Data & databases9186 installsUpdated 1d ago
Repository

About d3js-visualization

d3js-visualization is a skill that provides D3.js patterns for building data visualizations. It covers common charts, interactive behavior, data formatting, performance, and accessibility.

Key features

  1. 01Data binding and scales
  2. 02Axes and common chart types
  3. 03Tooltips and transitions
  4. 04Zoom and pan interactions
  5. 05Performance and accessibility guidance

Use cases

  1. 01Building bar, line, or pie charts
  2. 02Adding interaction to charts
  3. 03Improving chart performance and accessibility

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill d3js-visualization

After installing, type/d3js-visualizationin Claude Code to confirm it loaded.

Bundled files_meta.json

Details

From the project README

D3.js Visualization

Core Concepts

Selection and Data Binding
// 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));
Scales
// 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);
Axes
// 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);

Common Chart Types

Bar Chart
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();
}
Line Chart
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();
}
Pie/Donut Chart
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();
}

Interactivity

Tooltips
// 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');
  });
Transitions
// 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);
Zoom and Pan
const zoom = d3.zoom()
  .scaleExtent([1, 8])
  .on('zoom', (event) => {
    g.attr('transform', event.transform);
  });

svg.call(zoom);

Best Practices

Performance
  • Use join() instead of enter/update/exit for cleaner code
  • Throttle resize handlers
  • Use CSS for simple styling
  • Avoid excessive DOM updates
Accessibility
  • Add aria-label to SVG
  • Use role="img" for decorative charts
  • Provide data tables as alternatives
  • Ensure sufficient color contrast
Data Formatting
// 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%');

Comments

Markdown supported