Claude D3.js Skill: мастер кастомных визуализаций

10.03.2026обновлено 2 дней назад

Забудьте про Chart.js и Highcharts. Если вам нужны действительно уникальные, интерактивные визуализации с полным контролем над каждым пикселем — вам нужен claude-d3js-skill от Chris von Csefalvay.

Что это за магия

D3.js (Data-Driven Documents) — это библиотека, которая связывает данные с DOM-элементами и применяет data-driven трансформации. Claude D3.js Skill превращает Claude Code в эксперта по созданию кастомных визуализаций любой сложности.

Что умеет:

  • Chord diagrams, heatmaps, force-directed networks
  • Интерактивные карты с кастомными проекциями
  • Анимированные переходы и хореографированные трансформации
  • Публикационно-качественная графика с контролем каждого элемента
  • Интеграция с React, Vue, Svelte, ванильным JS

Установка и настройка

Скилл находится в /skills/claude-d3js-skill/

Быстрый старт:

# NPM
npm install d3

# CDN (для прототипов)
<script src="https://d3js.org/d3.v7.min.js"></script>

# ES6 import
import * as d3 from 'd3';

Структура проекта:

claude-d3js-skill/
├── SKILL.md              # Полная документация
├── references/
│   ├── d3-patterns.md    # Библиотека паттернов
│   ├── scale-reference.md # Справочник по scales
│   └── colour-schemes.md  # Цветовые палитры
└── assets/
    ├── chart-template.js      # Базовый темплейт
    ├── interactive-template.js # С интерактивностью
    └── sample-data.json       # Тестовые данные

Два подхода к интеграции

Паттерн A: Direct DOM manipulation (рекомендуется)

D3 напрямую манипулирует DOM — классика для сложных визуализаций:

function drawChart(data) {
  if (!data || data.length === 0) return;

  const svg = d3.select('#chart');
  svg.selectAll("*").remove(); // Очистка

  const width = 800, height = 400;
  const margin = { top: 20, right: 30, bottom: 40, left: 50 };
  
  const g = svg.append("g")
    .attr("transform", `translate(${margin.left},${margin.top})`);

  // Scales, оси, визуализация...
}

// Вызов при изменении данных
drawChart(myData);

Паттерн B: Declarative rendering (для фреймворков)

D3 только для вычислений, отрисовка через фреймворк:

function getChartElements(data) {
  const xScale = d3.scaleLinear()
    .domain([0, d3.max(data, d => d.value)])
    .range([0, 400]);

  return data.map((d, i) => ({
    x: 50, y: i * 30,
    width: xScale(d.value), height: 25
  }));
}

// React: {getChartElements(data).map((d, i) => 
//   <rect key={i} {...d} fill="steelblue" />)}
// Vue: v-for directive
// Svelte: {#each getChartElements(data) as d}

Стандартная структура визуализации

Claude D3.js Skill учит структурировать код по стандартному паттерну:

function drawVisualization(data) {
  if (!data || data.length === 0) return;

  const svg = d3.select('#chart');
  svg.selectAll("*").remove();

  // 1. Define dimensions
  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;

  // 2. Create main group with margins
  const g = svg.append("g")
    .attr("transform", `translate(${margin.left},${margin.top})`);

  // 3. Create 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]); // Инвертировано для SVG

  // 4. Create axes
  g.append("g")
    .attr("transform", `translate(0,${innerHeight})`)
    .call(d3.axisBottom(xScale));

  g.append("g")
    .call(d3.axisLeft(yScale));

  // 5. Bind data and create 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");
}

Этот паттерн универсален: margin convention, scales, axes, data binding — основа любой D3 визуализации.

Продвинутые паттерны

Chord Diagram — связи между сущностями:

function drawChordDiagram(data) {
  // data: [{ source: 'A', target: 'B', value: 10 }, ...]
  
  const nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])));
  const matrix = Array(nodes.length).fill().map(() => Array(nodes.length).fill(0));

  // Заполняем матрицу
  data.forEach(d => {
    const i = nodes.indexOf(d.source);
    const j = nodes.indexOf(d.target);
    matrix[i][j] += d.value;
    matrix[j][i] += d.value;
  });

  // Chord layout
  const chord = d3.chord().padAngle(0.05);
  const arc = d3.arc().innerRadius(200).outerRadius(230);
  const ribbon = d3.ribbon();

  const chords = chord(matrix);

  // Рисуем ribbons (связи)
  g.selectAll("path")
    .data(chords)
    .join("path")
    .attr("d", ribbon)
    .attr("fill", d => colorScale(nodes[d.source.index]));

  // Рисуем arcs (узлы)
  g.selectAll("g")
    .data(chords.groups)
    .join("g")
    .append("path")
    .attr("d", arc)
    .attr("fill", d => colorScale(nodes[d.index]));
}

Heatmap — цветовое кодирование значений:

function drawHeatmap(data) {
  // data: [{ row: 'A', column: 'X', value: 10 }, ...]
  
  const rows = Array.from(new Set(data.map(d => d.row)));
  const columns = Array.from(new Set(data.map(d => d.column)));

  const xScale = d3.scaleBand().domain(columns).range([0, width]).padding(0.01);
  const yScale = d3.scaleBand().domain(rows).range([0, height]).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));

  // Color legend
  const legendScale = d3.scaleLinear()
    .domain(colorScale.domain())
    .range([legendHeight, 0]);
  
  // Gradient в legend
  for (let i = 0; i < legendHeight; i++) {
    legend.append("rect")
      .attr("y", i).attr("width", 20).attr("height", 1)
      .attr("fill", colorScale(legendScale.invert(i)));
  }
}

Force-directed Network — живая сеть узлов:

const simulation = d3.forceSimulation(nodes)
  .force("link", d3.forceLink(links).id(d => d.id))
  .force("charge", d3.forceManyBody().strength(-300))
  .force("center", d3.forceCenter(width / 2, height / 2));

// Узлы с драг-дропом
const node = g.selectAll("circle")
  .data(nodes)
  .join("circle")
  .attr("r", 8)
  .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

// Симуляция обновляет позиции
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);
});

Интерактивность и анимации

Tooltips:

const tooltip = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style("position", "absolute")
  .style("visibility", "hidden");

circles
  .on("mouseover", (event, d) => {
    tooltip.style("visibility", "visible")
           .html(`<strong>${d.label}</strong><br/>Value: ${d.value}`);
  })
  .on("mousemove", (event) => {
    tooltip.style("top", (event.pageY - 10) + "px")
           .style("left", (event.pageX + 10) + "px");
  })
  .on("mouseout", () => tooltip.style("visibility", "hidden"));

Zoom & Pan:

const zoom = d3.zoom()
  .scaleExtent([0.5, 10])
  .on("zoom", (event) => {
    g.attr("transform", event.transform);
  });

svg.call(zoom);

Smooth transitions:

// Базовая анимация
circles.transition().duration(750).attr("r", 10);

// Цепочка анимаций
circles.transition().duration(500).attr("fill", "orange")
      .transition().duration(500).attr("r", 15);

// Staggered animation
circles.transition()
       .delay((d, i) => i * 50)
       .duration(500)
       .ease(d3.easeBounceOut)
       .attr("cy", d => yScale(d.value));

Responsive дизайн

Скилл учит делать визуализации адаптивными:

function setupResponsiveChart(containerId, data) {
  const container = document.getElementById(containerId);
  const svg = d3.select(`#${containerId}`).append('svg');

  function updateChart() {
    const { width, height } = container.getBoundingClientRect();
    svg.attr('width', width).attr('height', height);
    drawChart(data, svg, width, height);
  }

  updateChart();
  window.addEventListener('resize', updateChart);
  
  return () => window.removeEventListener('resize', updateChart);
}

// Или через ResizeObserver (лучше)
const observer = new ResizeObserver(() => {
  const { width, height } = svgElement.getBoundingClientRect();
  redrawChart(width, height);
});
observer.observe(containerElement);

Scales Reference

Скилл включает comprehensive guide по D3 scales:

Quantitative:

  • d3.scaleLinear() — линейная
  • d3.scaleLog() — логарифмическая
  • d3.scalePow() — степенная
  • d3.scaleTime() — временная

Ordinal:

  • d3.scaleBand() — для bar charts
  • d3.scalePoint() — для line/scatter категорий
  • d3.scaleOrdinal() — для цветов

Sequential:

  • d3.scaleSequential() — градиент цветов
  • d3.scaleDiverging() — diverging palette

Пример:

// Band scale для bar chart
const xScale = d3.scaleBand()
  .domain(['Q1', 'Q2', 'Q3', 'Q4'])
  .range([0, 400])
  .padding(0.1);

// Sequential для heatmap
const colorScale = d3.scaleSequential(d3.interpolateBlues)
  .domain([0, 100]);

// Ordinal для категорий
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);

Best Practices от скилла

Data preparation:

// Валидация данных
const cleanData = data.filter(d => d.value != null && !isNaN(d.value));

// Парсинг дат
const parseDate = d3.timeParse("%Y-%m-%d");
const parsedData = data.map(d => ({...d, date: parseDate(d.date)}));

Performance для больших datasets:

// Canvas instead of SVG для >1000 элементов
// Quadtree для collision detection
// Virtual scrolling для больших списков
// requestAnimationFrame для кастомных анимаций

Accessibility:

svg.attr("role", "img")
   .attr("aria-label", "Bar chart showing quarterly revenue");

svg.append("title").text("Quarterly Revenue 2024");
svg.append("desc").text("Revenue growth across quarters");

Интеграция с фреймворками

React Hook:

function useD3(renderChartFn, dependencies) {
  const ref = useRef();
  
  useEffect(() => {
    renderChartFn(d3.select(ref.current));
  }, dependencies);
  
  return ref;
}

// В компоненте
const chartRef = useD3(
  (svg) => drawChart(svg, data),
  [data]
);

return <svg ref={chartRef}></svg>;

Vue Composition API:

import { ref, onMounted, watch } from 'vue';

export default {
  setup() {
    const chartRef = ref(null);
    
    const drawChart = () => {
      const svg = d3.select(chartRef.value);
      // D3 code here...
    };
    
    onMounted(drawChart);
    watch(() => data.value, drawChart);
    
    return { chartRef };
  }
};

Svelte:

<script>
  import * as d3 from 'd3';
  
  export let data;
  let chartElement;
  
  $: if (chartElement && data) {
    const svg = d3.select(chartElement);
    // D3 code here...
  }
</script>

<svg bind:this={chartElement}></svg>

Когда использовать D3.js

Use D3.js for:

  • Кастомные визуализации, недоступные в готовых библиотеках
  • Интерактивные исследования с pan/zoom/brush
  • Network/graph визуализации
  • Географические карты с кастомными проекциями
  • Публикационно-качественная графика
  • Анимированные storytelling визуализации

Consider alternatives:

  • Chart.js/Highcharts** — для стандартных charts
  • Three.js** — для 3D визуализаций
  • Observable Plot** — для быстрых exploratory visualizations
  • Plotly** — для scientific plotting

Заключение

Claude D3.js Skill — это мастер-класс по созданию уникальных, интерактивных визуализаций. Вместо борьбы с ограничениями готовых библиотек вы получаете полный контроль над каждым пикселем.

Что делает его особенным:

  • Comprehensive documentation с living examples
  • Готовые templates и boilerplate
  • Integration patterns для всех популярных фреймворков
  • Performance optimizations для больших datasets
  • Accessibility guidelines

GitHub: https://github.com/chrisvoncsefalvay/claude-d3js-skill

Установите скилл, и ваш Claude Code станет D3.js-мастером, способным создавать визуализации уровня New York Times или Observable.


*Data-driven documents: когда данные становятся искусством.* 🧪

TG

> Пока нет комментариев