К основному содержанию
правило

d3 — правило для Cursor: лучшие практики D3.js v7+

Правило для Cursor задаёт, как писать код визуализаций на D3.js v7+: модульные импорты, selection.join() вместо ручного enter/update/exit, стили через CSS.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/d3.mdc
Открыть на GitHub

Что делает d3

Правило подключается к файлам **/*.{js,jsx} и работает как инструкция для агента: он держит структуру визуализации разделённой на функции — загрузка данных, шкалы, оси, отрисовка элементов — вместо одной длинной простыни.

Главное требование — импортировать только нужные модули (d3-selection, d3-scale, d3-axis, d3-array), а не всю библиотеку целиком. Для привязки данных правило настаивает на selection.join() с ключевой функцией, а для зума, перетаскивания и выделения — на готовых модулях d3-zoom, d3-drag, d3-brush. Своя обработка mousedown и mousemove для зума — прямой путь к багам, и правило это отдельно проговаривает.

Визуальные свойства вроде fill, stroke и font-size выносятся в CSS-классы, а не в attr(). Если в проекте уже есть инлайновые стили на D3-элементах, агент начнёт их переписывать — это повод проверить дифф перед коммитом.

Какие задачи решает

  • разбить визуализацию на отдельные функции вместо одного длинного модуля
  • перейти с ручного enter/update/exit на selection.join()
  • уменьшить бандл, заменив импорт всей библиотеки на точечные модули
  • вынести цвета и размеры шрифтов из attr() в CSS-классы
  • подключить d3-zoom вместо самописной обработки событий мыши

Примеры запросов агенту

  • Перепиши этот график на D3 v7: импортируй только нужные модули и используй selection.join()
  • Раздели код визуализации на функции: данные, шкалы, оси, отрисовка
  • Убери инлайновые fill и font-size, заведи CSS-классы для осей

Как установить d3

Вручную: положить файлы

.cursor/rules/d3.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о d3

Как установить правило d3 в Cursor?

Создайте файл .cursor/rules/d3.mdc и вставьте в него содержимое правила. Cursor подхватит его для файлов, подходящих под glob **/*.{js,jsx}.

Что делает правило d3?

Задаёт агенту набор практик для D3.js v7+: модульные импорты, selection.join() для привязки данных, готовые модули поведения для зума и перетаскивания, стили через внешний CSS.

На какие файлы распространяется правило?

На JavaScript и JSX-файлы по маске **/*.{js,jsx}. В проектах с TypeScript правило не сработает, пока вы не расширите glob.

Исходный текст

d3.mdc · CC0-1.0
---
description: This guide provides definitive, actionable best practices for writing modern D3.js v7+ code, focusing on modularity, performance, and maintainability.
globs: **/*.{js,jsx}
---
# d3 Best Practices

D3.js is a powerful, low-level library for data visualization. To leverage its full potential and maintain a clean, performant codebase, adhere to these definitive guidelines.

## 1. Code Organization and Structure

Always structure your D3 visualizations with clear separation of concerns. This enhances readability, testability, and reusability.

### 1.1. Modular Imports

Import only the specific D3 modules you need. This is critical for bundle size optimization and tree-shaking.

❌ **BAD: Over-importing**
```javascript
// Imports the entire D3 library, increasing bundle size unnecessarily.
import * as d3 from 'd3'; 

// Or using a CDN for the full library
// <script src="https://d3js.org/d3.v7.min.js"></script>
```

✅ **GOOD: Targeted Imports**
```javascript
// Import only necessary modules for a simple bar chart
import { select } from 'd3-selection';
import { scaleLinear, scaleBand } from 'd3-scale';
import { axisBottom, axisLeft } from 'd3-axis';
import { max } from 'd3-array';
```

### 1.2. Functional Encapsulation

Organize your visualization logic into distinct, focused functions. A single function should handle data loading, another for scale creation, another for axis rendering, and so on.

```javascript
// chart.js
import { select } from 'd3-selection';
import { scaleLinear, scaleBand } from 'd3-scale';
import { axisBottom, axisLeft } from 'd3-axis';
import { max } from 'd3-array';

export function createBarChart(containerSelector, data, options = {}) {
  const { width = 800, height = 500, 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 = select(containerSelector)
    .append('svg')
    .attr('viewBox', `0 0 ${width} ${height}`) // Responsive design
    .attr('role', 'img')
    .attr('aria-label', 'Bar chart showing data distribution');

  const g = svg.append('g')
    .attr('transform', `translate(${margin.left},${margin.top})`);

  // 1. Create Scales
  const xScale = scaleBand()
    .domain(data.map(d => d.category))
    .range([0, innerWidth])
    .padding(0.1);

  const yScale = scaleLinear()
    .domain([0, max(data, d => d.value)])
    .range([innerHeight, 0]);

  // 2. Render Axes
  g.append('g')
    .attr('class', 'x-axis') // Style with CSS
    .attr('transform', `translate(0,${innerHeight})`)
    .call(axisBottom(xScale));

  g.append('g')
    .attr('class', 'y-axis') // Style with CSS
    .call(axisLeft(yScale));

  // 3. Draw Elements (Bars)
  g.selectAll('.bar')
    .data(data)
    .join('rect')
      .attr('class', 'bar')
      .attr('x', d => xScale(d.category))
      .attr('y', d => yScale(d.value))
      .attr('width', xScale.bandwidth())
      .attr('height', d => innerHeight - yScale(d.value));

  // Add labels, title, etc. as separate concerns
}

// main.js
import { createBarChart } from './chart.js';

async function init() {
  const chartData = await fetch('/api/data').then(res => res.json());
  createBarChart('#chart-container', chartData);
}

init();
```

## 2. Common Patterns and Anti-patterns

Embrace D3's idiomatic patterns for data binding and DOM manipulation.

### 2.1. The `selection.join()` Pattern

Always use `selection.join()` for data-driven element creation, update, and removal. It is the most concise and efficient way to handle the enter, update, and exit selections.

❌ **BAD: Manual Enter/Update/Exit (D3 v3/v4 style)**
```javascript
const bars = g.selectAll('.bar')
  .data(data);

// Exit
bars.exit().remove();

// Enter
bars.enter().append('rect')
  .attr('class', 'bar')
  .attr('x', d => xScale(d.category))
  .attr('width', xScale.bandwidth())
  .attr('y', innerHeight) // Initial position for animation
  .attr('height', 0) // Initial height for animation
  .transition().duration(500) // Animate enter
    .attr('y', d => yScale(d.value))
    .attr('height', d => innerHeight - yScale(d.value));

// Update (merged selection)
bars.transition().duration(500)
  .attr('x', d => xScale(d.category))
  .attr('y', d => yScale(d.value))
  .attr('width', xScale.bandwidth())
  .attr('height', d => innerHeight - yScale(d.value));
```

✅ **GOOD: `selection.join()` (D3 v5+ style)**
```javascript
g.selectAll('.bar')
  .data(data, d => d.category) // Use a key function for stable joins
  .join(
    enter => enter.append('rect')
      .attr('class', 'bar')
      .attr('x', d => xScale(d.category))
      .attr('y', innerHeight) // Start from bottom for animation
      .attr('width', xScale.bandwidth())
      .attr('height', 0) // Start with zero height for animation
      .call(enter => enter.transition().duration(500)
        .attr('y', d => yScale(d.value))
        .attr('height', d => innerHeight - yScale(d.value))),
    update => update.transition().duration(500)
      .attr('x', d => xScale(d.category))
      .attr('y', d => yScale(d.value))
      .attr('width', xScale.bandwidth())
      .attr('height', d => innerHeight - yScale(d.value)),
    exit => exit.transition().duration(500)
      .attr('y', innerHeight) // Animate to bottom
      .attr('height', 0) // Animate to zero height
      .remove()
  );
```

### 2.2. External CSS for Styling

Style D3 elements using external CSS classes. Avoid inline `style()` or `attr()` calls for visual properties like `fill`, `stroke`, `font-size`. This improves maintainability, themeability, and accessibility.

❌ **BAD: Inline Styling**
```javascript
g.append('g')
  .attr('transform', `translate(0,${innerHeight})`)
  .call(axisBottom(xScale))
  .selectAll('text')
    .attr('font-size', '12px')
    .attr('fill', '#333');
```

✅ **GOOD: CSS Classes**
```javascript
// In your JavaScript
g.append('g')
  .attr('class', 'x-axis') // Apply a class
  .attr('transform', `translate(0,${innerHeight})`)
  .call(axisBottom(xScale));

// In your CSS file (e.g., style.css)
.x-axis text {
  font-size: 12px;
  fill: #333;
}
.x-axis path, .x-axis line {
  stroke: currentColor; /* Inherits color from parent */
}
```

### 2.3. D3 Behaviors for Interaction

For common interactions like zooming, panning, and dragging, use D3's dedicated behavior modules (`d3-zoom`, `d3-drag`, `d3-brush`). They handle complex event logic and browser quirks robustly.

❌ **BAD: Manual Zoom/Pan Implementation**
```javascript
// Don't try to reimplement zoom/pan with raw mouse events.
svg.on('mousedown', handleMouseDown);
svg.on('mousemove', handleMouseMove);
// ... this path leads to bugs and frustration.
```

✅ **GOOD: Using `d3-zoom`**
```javascript
import { zoom, zoomIdentity } from 'd3-zoom';
import { select } from 'd3-selection';

function zoomed({ transform }) {
  // Apply the transform to your chart elements
  g.attr('transform', transform);
  // Update axes if necessary
  xAxisGroup.call(axisBottom(transform.rescaleX(xScale)));
  yAxisGroup.call(axisLeft(transform.rescaleY(yScale)));
}

const chartZoom = zoom()
  .scaleExtent([0.5, 10]) // Min/max zoom level
  .translateExtent([[0, 0], [width, height]]) // Pan boundaries
  .on('zoom', zoomed);

select('#chart-container svg')
  .call(chartZoom)
  .call(chartZoom.transform, zoomIdentity); // Initialize zoom
```

## 3. Performance Considerations

Optimize D3 charts for smooth interactions and efficient rendering, especially with large datasets.

### 3.1. Use `viewBox` for Responsiveness

Always use the `viewBox` attribute on your SVG element for responsive scaling. Avoid manually recalculating and setting `width`/`height` attributes on resize events.

❌ **BAD: Fixed Dimensions, Manual Resize Handling**
```javascript
const svg = d3.select('#chart').append('svg')
  .attr('width', 960)
  .attr('height', 500);

// Requires complex resize observer logic to update width/height and redraw
window.addEventListener('resize', () => { /* ... */ });
```

✅ **GOOD: `viewBox` and CSS**
```javascript
// In your JavaScript
const svg = d3.select('#chart').append('svg')
  .attr('viewBox', `0 0 ${width} ${height}`); // Logical dimensions

// In your CSS
#chart svg {
  display: block; /* Remove extra space below SVG */
  width: 100%;    /* Scale to container width */
  height: auto;   /* Maintain aspect ratio */
}
```

### 3.2. Minimize DOM Operations

Batch DOM updates and avoid unnecessary re-renders. `selection.join()` inherently helps with this by efficiently managing enter/update/exit.

### 3.3. Consider Canvas for Large Datasets

For extremely large datasets (thousands or millions of elements), SVG can become slow. D3 can draw to HTML Canvas elements, which offer superior performance for high-volume rendering.

```javascript
// Example: Drawing a scatter plot on Canvas
import { select } from 'd3-selection';
// ... other D3 modules

const canvas = select('#chart-container')
  .append('canvas')
  .attr('width', width)
  .attr('height', height)
  .node();

const context = canvas.getContext('2d');

function drawCanvasChart(data) {
  context.clearRect(0, 0, width, height);
  context.fillStyle = 'steelblue';
  data.forEach(d => {
    context.beginPath();
    context.arc(xScale(d.x), yScale(d.y), 3, 0, 2 * Math.PI);
    context.fill();
  });
}

// Call drawCanvasChart(data) whenever data or scales change.
```

## 4. Common Pitfalls and Gotchas

Avoid these common mistakes to prevent bugs and maintain a robust D3 application.

### 4.1. Forgetting Key Functions with `data()`

When updating data, always provide a key function to `selection.data(newData, d => d.id)`. This tells D3 how to uniquely identify data points, ensuring stable joins and correct element transitions.

❌ **BAD: No Key Function (unstable joins)**
```javascript
// If data order changes or items are added/removed, D3 might re-bind incorrectly.
g.selectAll('.bar').data(data)
  .join(/* ... */);
```

✅ **GOOD: With Key Function**
```javascript
// D3 uses d.category to match old and new data points.
g.selectAll('.bar').data(data, d => d.category)
  .join(/* ... */);
```

### 4.2. Incorrectly Updating Scales and Axes

When data or dimensions change, you must update the scales' domains/ranges *before* calling the axis generators again.

❌ **BAD: Updating Axes without updating Scales**
```javascript
// This will render axes based on old scale domains/ranges.
xAxisGroup.call(axisBottom(xScale)); // xScale's domain/range might be stale
```

✅ **GOOD: Update Scales, then Axes**
```javascript
xScale.domain(data.map(d => d.category));
yScale.domain([0, max(data, d => d.value)]);

xAxisGroup.transition().duration(500).call(axisBottom(xScale));
yAxisGroup.transition().duration(500).call(axisLeft(yScale));
```

### 4.3. Ignoring Accessibility (ARIA Attributes)

D3 charts are often visual. For screen readers and other assistive technologies, add semantic SVG elements and ARIA attributes.

```javascript
const svg = select(containerSelector)
  .append('svg')
  .attr('role', 'img') // Indicate it's an image
  .attr('aria-label', 'Bar chart showing monthly sales data'); // Descriptive label

// For individual elements
g.selectAll('.bar')
  .data(data, d => d.category)
  .join('rect')
    .attr('aria-label', d => `Sales for ${d.category}: ${d.value}`);
```

## 5. Testing Approaches

Ensure the reliability and correctness of your D3 visualizations.

### 5.1. Unit Testing Pure Functions

Test your data transformation, scale creation, and utility functions independently. These are typically pure functions, making them easy to test.

```javascript
// data-utils.js
export function processRawData(rawData) {
  return rawData.map(d => ({
    category: d.name,
    value: +d.amount // Ensure numeric type
  }));
}

// data-utils.test.js (using Jest)
import { processRawData } from './data-utils';

test('processRawData converts amount to number', () => {
  const raw = [{ name: 'A', amount: '100' }];
  const processed = processRawData(raw);
  expect(processed[0].value).toBe(100);
});
```

### 5.2. Visual Regression Testing

For visual output, implement visual regression tests using tools like Playwright or Cypress. These tools capture screenshots of your charts and compare them against baseline images, flagging any unintended visual changes.

```javascript
// cypress/e2e/chart.cy.js
describe('Bar Chart Visuals', () => {
  it('should render correctly', () => {
    cy.visit('/charts/bar-chart'); // Your chart page
    cy.get('#chart-container svg').compareSnapshot('bar-chart-initial');
  });

  it('should update correctly on data change', () => {
    cy.visit('/charts/bar-chart?data=updated'); // Page with updated data
    cy.get('#chart-container svg').compareSnapshot('bar-chart-updated');
  });
});
```

### 5.3. Observable Notebooks for Prototyping

Use Observable notebooks for rapid prototyping, sharing, and testing D3 code snippets. They provide an interactive environment to validate visual output and explore D3 patterns.

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Все правила IDE по теме «Фронтенд и UI» →
typescript-data-visualization-engineering официальный OpenAI Помогает проектировать архитектуру визуализации данных на TypeScript: от строгих схем и разделения слоёв до подбора библиотек вроде D3, Three.js или Canvas. CX Codex GP ChatGPT 9установок react-and-nextjs-data-visualization официальный OpenAI Инструкции по встраиванию графиков и диаграмм в React и Next.js: границы SSR, разделение состояния и отрисовки, адаптивность и защита от ошибок гидратации. CX Codex GP ChatGPT 13установок d3-visualization nexu-io Скилл учит агента создавать интерактивные графики и визуализации данных на D3.js. Подойдёт, если нужно быстро генерировать диаграммы для дашбордов, отчётов и прототипов. CX Codex +1 3 100установок mapbox-data-visualization-patterns mapbox Готовые шаблоны визуализации данных на картах Mapbox для Claude Code: картограммы, тепловые слои и 3D-модели. Пригодится при отрисовке статистики и плотности точек. CX Codex +1 2 100установок performance-optimization Mindrally Набор правил для веб-производительности: серверный рендеринг, CSS, JavaScript, ассеты и кэш. Полезен, когда агент пишет фронтенд и вы хотите задать ему планку. CX Codex +1 848установок modern-web-guidance GoogleChrome Скилл ищет актуальные гайды по HTML, CSS и клиентскому JS и подставляет их в работу агента. Пригодится, когда веб-API успели устареть в обучающих данных модели. CX Codex +1 35 200установок

Из того же набора69

drizzle Правило задаёт агенту Cursor стиль работы с Drizzle ORM: модульные схемы, явные ограничения полей и единый нейминг в TypeScript-проекте. ★ 3 579звёзд репо django-rest-framework Правило Cursor, которое задаёт агенту единый стиль разработки API на Django REST Framework: структура проекта, сериализаторы, ViewSets. ★ 3 579звёзд репо digitalocean Правило для Cursor задаёт, как строить и разворачивать приложения в DigitalOcean: VPC, managed-сервисы, контейнеры. ★ 3 579звёзд репо datadog Правило для Cursor задаёт стандарты Datadog: обязательные теги service, env и version, структурные логи и порядок в именах метрик. ★ 3 579звёзд репо cuda Правило задаёт Cursor стиль работы с CUDA C++: проверка ошибок API, разделение host и device кода, оптимизация ядер. Пригодится, если пишете GPU-код и устали от молчаливых падений. ★ 3 579звёзд репо codemirror Правило для Cursor задаёт, как писать редакторы на CodeMirror 6: модульные импорты, неизменяемое состояние, конфигурация через расширения. ★ 3 579звёзд репо clerk Правило для Cursor задаёт жёсткие требования к аутентификации Clerk: PKCE, серверные проверки, короткие токены. Пригодится, когда в проекте уже есть Clerk и нужен порядок в безопасности. ★ 3 579звёзд репо chakra-ui Правило для Cursor задаёт, как писать интерфейсы на Chakra UI v2: тема через токены, варианты компонентов через рецепты, стилевые пропсы вместо CSS. ★ 3 579звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой