d3js-data-visualization
Create interactive, custom data visualizations using d3.js — including charts, graphs, network diagrams, and geographic maps. Use when you need fine-grained control over visual elements, transitions, or interactions beyond what standard charting libraries offer, in any JavaScript environment (vanilla JS, React, Vue, Svelte, etc.).
git clone --depth 1 https://github.com/besoeasy/open-skills /tmp/d3js-data-visualization && cp -r /tmp/d3js-data-visualization/skills/d3js-data-visualization ~/.claude/skills/d3js-data-visualizationSKILL.md
# D3.js Data Visualization
Build sophisticated, interactive data visualizations using d3.js (Data-Driven Documents). D3 binds data to DOM elements and applies data-driven transformations to produce publication-quality, fully customizable visuals.
## When to Use This Skill
- Custom charts requiring unique visual encodings or layouts
- Interactive visualizations with pan, zoom, or brush behaviors
- Network/graph visualizations (force-directed, tree, hierarchy, chord diagrams)
- Geographic visualizations with custom projections
- Smooth, choreographed transitions and animations
- Novel chart types not available in standard libraries (Recharts, Chart.js, etc.)
- Fine-grained SVG styling and accessibility control
**Consider alternatives for:**
- 3D visualizations → use Three.js
- Simple standard charts with minimal customization → use Chart.js or Recharts
## Required Tools / Libraries
No backend required. Runs entirely in the browser or Node.js (with jsdom/canvas).
```bash
# Install via npm
npm install d3
# Or use CDN in HTML
<script src="https://d3js.org/d3.v7.min.js"></script>
```
## Core Workflow
### 1. Set Up D3
```javascript
import * as d3 from 'd3';
```
### 2. Standard Chart Structure
Every d3 visualization follows this pattern:
```javascript
function drawChart(data) {
if (!data || data.length === 0) return;
const svg = d3.select('#chart');
svg.selectAll("*").remove(); // clear previous render
const width = 800, height = 400;
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Define scales
const xScale = d3.scaleLinear().domain([0, d3.max(data, d => d.x)]).range([0, innerWidth]);
const yScale = d3.scaleLinear().domain([0, d3.max(data, d => d.y)]).range([innerHeight, 0]);
// Axes
g.append("g").attr("transform", `translate(0,${innerHeight})`).call(d3.axisBottom(xScale));
g.append("g").call(d3.axisLeft(yScale));
// Data elements
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(d.y))
.attr("r", 5)
.attr("fill", "steelblue");
}
```
## Common Chart Patterns
### Bar Chart
```javascript
const xScale = d3.scaleBand().domain(data.map(d => d.category)).range([0, innerWidth]).padding(0.1);
const yScale = d3.scaleLinear().domain([0, d3.max(data, d => d.value)]).range([innerHeight, 0]);
g.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => xScale(d.category))
.attr("y", d => yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => innerHeight - yScale(d.value))
.attr("fill", "steelblue");
```
### Line Chart
```javascript
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value))
.curve(d3.curveMonotoneX);
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2)
.attr("d", line);
```
### Scatter Plot
```javascript
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(d.y))
.attr("r", d => sizeScale(d.size))
.attr("fill", d => colorScale(d.category))
.attr("opacity", 0.7);
```
### Pie / Donut Chart
```javascript
const pie = d3.pie().value(d => d.value).sort(null);
const arc = d3.arc().innerRadius(0).outerRadius(Math.min(width, height) / 2 - 20);
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
const g = svg.append("g").attr("transform", `translate(${width / 2},${height / 2})`);
g.selectAll("path")
.data(pie(data))
.join("path")
.attr("d", arc)
.attr("fill", (d, i) => colorScale(i))
.attr("stroke", "white")
.attr("stroke-width", 2);
```
### Force-Directed Network Graph
```javascript
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(width / 2, height / 2));
const link = g.selectAll("line").data(links).join("line").attr("stroke", "#999");
const node = g.selectAll("circle").data(nodes).join("circle")
.attr("r", 8).attr("fill", "steelblue")
.call(d3.drag()
.on("start", (e) => { if (!e.active) simulation.alphaTarget(0.3).restart(); e.subject.fx = e.subject.x; e.subject.fy = e.subject.y; })
.on("drag", (e) => { e.subject.fx = e.x; e.subject.fy = e.y; })
.on("end", (e) => { if (!e.active) simulation.alphaTarget(0); e.subject.fx = null; e.subject.fy = null; }));
simulation.on("tick", () => {
link.attr("x1", d => d.source.x).attr("y1", d => d.source.y)
.attr("x2", d => d.target.x).attr("y2", d => d.target.y);
node.attr("cx", d => d.x).attr("cy", d => d.y);
});
```
### Heatmap
```javascript
// data: [{ row, column, value }, ...]
const rows = [...new Set(data.map(d => d.row))];
const cols = [...new Set(data.map(d => d.column))];
const xScale = d3.scaleBand().domain(cols).range([0, innerWidth]).padding(0.01);
const yScale = d3.scaleBand().domain(rows).range([0, innerHeight]).padding(0.01);
const colorScale = d3.scaleSequential(d3.interpolateYlOrRd).domain([0, d3.max(data, d => d.value)]);
g.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => xScale(d.column))
.attr("y", d => yScale(d.row))
.attr("width", xScale.bandwidth())
.attr("height", yScale.bandwidth())
.attr("fill", d => colorScale(d.value));
```
## Interactivity
### Tooltips
```javascript
const tooltip = d3.select("body").append("div")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background", "white")
.style("border", "1px solid #ddd")
.style("padding", "10px")
.style("border-radius", "4px")
.style("pointer-events", "none");
elements
.on("mouseover", (event, d) => tooltip.style("visibility", "visible").html(`<strong>${d.label}</strong><br/>Value: ${d.value}Encrypt and decrypt files or streams using age — a simple, modern, and secure encryption tool with small explicit keys, passphrase support, SSH key support, post-quantum hybrid keys, and UNIX-style composability. No config options, no footguns.
Upload and host files anonymously using decentralized storage with Originless and IPFS.
Automate web browsers for AI agents using agent-browser CLI with deterministic element selection.
Star all repositories from a GitHub user automatically. Use when: (1) Supporting open source creators, (2) Bulk discovery of useful projects, or (3) Automating GitHub engagement.
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Log all chat messages to a SQLite database for searchable history and audit. Use when: (1) Building chat history, (2) Auditing conversations, (3) Searching past messages, or (4) User asks to log chats.
Check cryptocurrency wallet balances across multiple blockchains using free public APIs.
Calculate line-of-sight and road distances between two cities using free OpenStreetMap services.