Smart D3 Data Visualization Engine
Streamline your workflow with this skill for create interactive charts and data visualizations. Built for Claude Code with best practices and real-world patterns.
D3 Data Visualization Engine
A data visualization skill for building interactive, publication-quality charts, maps, and data graphics using D3.js with responsive layouts and animated transitions.
When to Use
Choose D3 Data Visualization when:
- Building custom, interactive data visualizations beyond standard chart libraries
- Creating complex visualizations like force-directed graphs, treemaps, or geographic maps
- Implementing animated data transitions and interactive data exploration
- Building reusable chart components for dashboards and data products
Consider alternatives when:
- Standard charts (bar, line, pie) suffice — use Chart.js or Recharts
- Quick data exploration — use Observable notebooks or Jupyter
- No interactivity needed — use Matplotlib or ggplot2
Quick Start
npm install d3
import * as d3 from 'd3'; // Responsive bar chart function createBarChart(container: string, data: Array<{label: string; value: number}>) { const margin = { top: 20, right: 30, bottom: 40, left: 50 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = d3.select(container) .append('svg') .attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`) .append('g') .attr('transform', `translate(${margin.left},${margin.top})`); // Scales const x = d3.scaleBand() .domain(data.map(d => d.label)) .range([0, width]) .padding(0.2); const y = d3.scaleLinear() .domain([0, d3.max(data, d => d.value)!]) .nice() .range([height, 0]); // Axes svg.append('g') .attr('transform', `translate(0,${height})`) .call(d3.axisBottom(x)); svg.append('g') .call(d3.axisLeft(y).ticks(5)); // Bars with animation svg.selectAll('.bar') .data(data) .join('rect') .attr('class', 'bar') .attr('x', d => x(d.label)!) .attr('width', x.bandwidth()) .attr('y', height) // Start from bottom .attr('height', 0) .attr('fill', '#4472C4') .attr('rx', 4) .transition() .duration(800) .delay((_, i) => i * 100) .attr('y', d => y(d.value)) .attr('height', d => height - y(d.value)); // Tooltip const tooltip = d3.select(container) .append('div') .attr('class', 'tooltip') .style('opacity', 0) .style('position', 'absolute') .style('background', 'rgba(0,0,0,0.8)') .style('color', 'white') .style('padding', '8px 12px') .style('border-radius', '4px') .style('font-size', '14px'); svg.selectAll('.bar') .on('mouseover', (event, d) => { tooltip.transition().duration(200).style('opacity', 1); tooltip.html(`${d.label}: ${d.value}`) .style('left', event.pageX + 10 + 'px') .style('top', event.pageY - 28 + 'px'); d3.select(event.currentTarget).attr('fill', '#2a5599'); }) .on('mouseout', (event) => { tooltip.transition().duration(500).style('opacity', 0); d3.select(event.currentTarget).attr('fill', '#4472C4'); }); }
Core Concepts
D3 Core Modules
| Module | Purpose | Key Functions |
|---|---|---|
| d3-selection | DOM manipulation | select, selectAll, join |
| d3-scale | Data-to-visual mapping | scaleLinear, scaleBand, scaleOrdinal |
| d3-axis | Axis rendering | axisBottom, axisLeft |
| d3-shape | Shape generators | line, arc, area, pie |
| d3-transition | Animated transitions | transition, duration, ease |
| d3-geo | Geographic projections | geoPath, geoMercator |
| d3-hierarchy | Tree/network layouts | tree, treemap, pack |
| d3-force | Force-directed layouts | forceSimulation, forceLink |
Reusable Chart Pattern
function lineChart() { let width = 600; let height = 400; let xAccessor = (d: any) => d.x; let yAccessor = (d: any) => d.y; let color = '#4472C4'; function chart(selection: d3.Selection<any, any, any, any>) { selection.each(function(data) { const svg = d3.select(this).selectAll('svg') .data([data]).join('svg') .attr('viewBox', `0 0 ${width} ${height}`); const x = d3.scaleTime() .domain(d3.extent(data, xAccessor) as [Date, Date]) .range([50, width - 20]); const y = d3.scaleLinear() .domain([0, d3.max(data, yAccessor)!]) .nice() .range([height - 30, 20]); const line = d3.line<any>() .x(d => x(xAccessor(d))) .y(d => y(yAccessor(d))) .curve(d3.curveMonotoneX); svg.selectAll('.line') .data([data]).join('path') .attr('class', 'line') .attr('d', line) .attr('fill', 'none') .attr('stroke', color) .attr('stroke-width', 2); }); } // Chainable setters chart.width = (v: number) => { width = v; return chart; }; chart.height = (v: number) => { height = v; return chart; }; chart.x = (v: (d: any) => any) => { xAccessor = v; return chart; }; chart.y = (v: (d: any) => any) => { yAccessor = v; return chart; }; chart.color = (v: string) => { color = v; return chart; }; return chart; }
Configuration
| Option | Description | Default |
|---|---|---|
width | Chart width (or viewport-relative) | 800 |
height | Chart height | 400 |
margins | Chart margins object | { top: 20, right: 30, bottom: 40, left: 50 } |
color_scheme | D3 color scheme | d3.schemeCategory10 |
transition_duration | Animation duration (ms) | 800 |
responsive | Use viewBox for responsive sizing | true |
tooltip | Enable interactive tooltips | true |
grid_lines | Show background grid | false |
Best Practices
- Use viewBox instead of fixed width/height to make charts responsive —
viewBoxscales the SVG to fit its container while maintaining aspect ratio, eliminating the need for manual resize handlers - Implement the General Update Pattern with
.join()for data binding instead of the older.enter().append()pattern —.join()handles enter, update, and exit selections cleanly in one call - Create reusable chart functions using the module pattern with chainable setters so chart configuration is separate from data binding and rendering logic
- Add transitions for data changes to help users track how values change between states — animate from old positions to new ones rather than instantly replacing the visualization
- Use semantic color scales that convey meaning (red for negative, green for positive) and ensure accessibility by not relying solely on color to distinguish data series; add patterns, labels, or shapes as well
Common Issues
Transitions conflicting when data updates rapidly: Multiple transitions on the same element override each other, causing flickering. Use .interrupt() before starting new transitions, or use named transitions to prevent conflicts between different animation chains.
SVG performance with thousands of elements: Rendering 10,000+ SVG elements causes visible lag and poor interaction performance. Switch to Canvas rendering for large datasets, aggregate data before visualization, or use virtual rendering that only draws elements currently in the viewport.
Axis labels overlapping on small screens: Long category labels on the x-axis overlap when there are many data points. Rotate labels with .attr('transform', 'rotate(-45)'), truncate long text, or switch to a horizontal bar chart layout for many categories.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.