[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-220dbcc6-2240-4723-9b3a-2a6054bc5d42":3,"$f0KeURR8jc-468m2kqLDDGh-UwRpKqbrrphJPTPQ6Svg":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"220dbcc6-2240-4723-9b3a-2a6054bc5d42","claude-d3js-skill","这项技能提供了使用d3.js创建复杂、交互式数据可视化的指导。","cat_prod_data","mod_productivity","sickn33,productivity","---\nname: claude-d3js-skill\ndescription: \"This skill provides guidance for creating sophisticated, interactive data visualisations using d3.js.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# D3.js Visualisation\n\n## Overview\n\nThis skill provides guidance for creating sophisticated, interactive data visualisations using d3.js. D3.js (Data-Driven Documents) excels at binding data to DOM elements and applying data-driven transformations to create custom, publication-quality visualisations with precise control over every visual element. The techniques work across any JavaScript environment, including vanilla JavaScript, React, Vue, Svelte, and other frameworks.\n\n## When to use d3.js\n\n**Use d3.js for:**\n- Custom visualisations requiring unique visual encodings or layouts\n- Interactive explorations with complex pan, zoom, or brush behaviours\n- Network\u002Fgraph visualisations (force-directed layouts, tree diagrams, hierarchies, chord diagrams)\n- Geographic visualisations with custom projections\n- Visualisations requiring smooth, choreographed transitions\n- Publication-quality graphics with fine-grained styling control\n- Novel chart types not available in standard libraries\n\n**Consider alternatives for:**\n- 3D visualisations - use Three.js instead\n\n## Core workflow\n\n### 1. Set up d3.js\n\nImport d3 at the top of your script:\n\n```javascript\nimport * as d3 from 'd3';\n```\n\nOr use the CDN version (7.x):\n\n```html\n\u003Cscript src=\"https:\u002F\u002Fd3js.org\u002Fd3.v7.min.js\">\u003C\u002Fscript>\n```\n\nAll modules (scales, axes, shapes, transitions, etc.) are accessible through the `d3` namespace.\n\n### 2. Choose the integration pattern\n\n**Pattern A: Direct DOM manipulation (recommended for most cases)**\nUse d3 to select DOM elements and manipulate them imperatively. This works in any JavaScript environment:\n\n```javascript\nfunction drawChart(data) {\n  if (!data || data.length === 0) return;\n\n  const svg = d3.select('#chart'); \u002F\u002F Select by ID, class, or DOM element\n\n  \u002F\u002F Clear previous content\n  svg.selectAll(\"*\").remove();\n\n  \u002F\u002F Set up dimensions\n  const width = 800;\n  const height = 400;\n  const margin = { top: 20, right: 30, bottom: 40, left: 50 };\n\n  \u002F\u002F Create scales, axes, and draw visualisation\n  \u002F\u002F ... d3 code here ...\n}\n\n\u002F\u002F Call when data changes\ndrawChart(myData);\n```\n\n**Pattern B: Declarative rendering (for frameworks with templating)**\nUse d3 for data calculations (scales, layouts) but render elements via your framework:\n\n```javascript\nfunction getChartElements(data) {\n  const xScale = d3.scaleLinear()\n    .domain([0, d3.max(data, d => d.value)])\n    .range([0, 400]);\n\n  return data.map((d, i) => ({\n    x: 50,\n    y: i * 30,\n    width: xScale(d.value),\n    height: 25\n  }));\n}\n\n\u002F\u002F In React: {getChartElements(data).map((d, i) => \u003Crect key={i} {...d} fill=\"steelblue\" \u002F>)}\n\u002F\u002F In Vue: v-for directive over the returned array\n\u002F\u002F In vanilla JS: Create elements manually from the returned data\n```\n\nUse Pattern A for complex visualisations with transitions, interactions, or when leveraging d3's full capabilities. Use Pattern B for simpler visualisations or when your framework prefers declarative rendering.\n\n### 3. Structure the visualisation code\n\nFollow this standard structure in your drawing function:\n\n```javascript\nfunction drawVisualization(data) {\n  if (!data || data.length === 0) return;\n\n  const svg = d3.select('#chart'); \u002F\u002F Or pass a selector\u002Felement\n  svg.selectAll(\"*\").remove(); \u002F\u002F Clear previous render\n\n  \u002F\u002F 1. Define dimensions\n  const width = 800;\n  const height = 400;\n  const margin = { top: 20, right: 30, bottom: 40, left: 50 };\n  const innerWidth = width - margin.left - margin.right;\n  const innerHeight = height - margin.top - margin.bottom;\n\n  \u002F\u002F 2. Create main group with margins\n  const g = svg.append(\"g\")\n    .attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n  \u002F\u002F 3. Create scales\n  const xScale = d3.scaleLinear()\n    .domain([0, d3.max(data, d => d.x)])\n    .range([0, innerWidth]);\n\n  const yScale = d3.scaleLinear()\n    .domain([0, d3.max(data, d => d.y)])\n    .range([innerHeight, 0]); \u002F\u002F Note: inverted for SVG coordinates\n\n  \u002F\u002F 4. Create and append axes\n  const xAxis = d3.axisBottom(xScale);\n  const yAxis = d3.axisLeft(yScale);\n\n  g.append(\"g\")\n    .attr(\"transform\", `translate(0,${innerHeight})`)\n    .call(xAxis);\n\n  g.append(\"g\")\n    .call(yAxis);\n\n  \u002F\u002F 5. Bind data and create visual elements\n  g.selectAll(\"circle\")\n    .data(data)\n    .join(\"circle\")\n    .attr(\"cx\", d => xScale(d.x))\n    .attr(\"cy\", d => yScale(d.y))\n    .attr(\"r\", 5)\n    .attr(\"fill\", \"steelblue\");\n}\n\n\u002F\u002F Call when data changes\ndrawVisualization(myData);\n```\n\n### 4. Implement responsive sizing\n\nMake visualisations responsive to container size:\n\n```javascript\nfunction setupResponsiveChart(containerId, data) {\n  const container = document.getElementById(containerId);\n  const svg = d3.select(`#${containerId}`).append('svg');\n\n  function updateChart() {\n    const { width, height } = container.getBoundingClientRect();\n    svg.attr('width', width).attr('height', height);\n\n    \u002F\u002F Redraw visualisation with new dimensions\n    drawChart(data, svg, width, height);\n  }\n\n  \u002F\u002F Update on initial load\n  updateChart();\n\n  \u002F\u002F Update on window resize\n  window.addEventListener('resize', updateChart);\n\n  \u002F\u002F Return cleanup function\n  return () => window.removeEventListener('resize', updateChart);\n}\n\n\u002F\u002F Usage:\n\u002F\u002F const cleanup = setupResponsiveChart('chart-container', myData);\n\u002F\u002F cleanup(); \u002F\u002F Call when component unmounts or element removed\n```\n\nOr use ResizeObserver for more direct container monitoring:\n\n```javascript\nfunction setupResponsiveChartWithObserver(svgElement, data) {\n  const observer = new ResizeObserver(() => {\n    const { width, height } = svgElement.getBoundingClientRect();\n    d3.select(svgElement)\n      .attr('width', width)\n      .attr('height', height);\n\n    \u002F\u002F Redraw visualisation\n    drawChart(data, d3.select(svgElement), width, height);\n  });\n\n  observer.observe(svgElement.parentElement);\n  return () => observer.disconnect();\n}\n```\n\n## Common visualisation patterns\n\n### Bar chart\n\n```javascript\nfunction drawBarChart(data, svgElement) {\n  if (!data || data.length === 0) return;\n\n  const svg = d3.select(svgElement);\n  svg.selectAll(\"*\").remove();\n\n  const width = 800;\n  const height = 400;\n  const margin = { top: 20, right: 30, bottom: 40, left: 50 };\n  const innerWidth = width - margin.left - margin.right;\n  const innerHeight = height - margin.top - margin.bottom;\n\n  const g = svg.append(\"g\")\n    .attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n  const xScale = d3.scaleBand()\n    .domain(data.map(d => d.category))\n    .range([0, innerWidth])\n    .padding(0.1);\n\n  const yScale = d3.scaleLinear()\n    .domain([0, d3.max(data, d => d.value)])\n    .range([innerHeight, 0]);\n\n  g.append(\"g\")\n    .attr(\"transform\", `translate(0,${innerHeight})`)\n    .call(d3.axisBottom(xScale));\n\n  g.append(\"g\")\n    .call(d3.axisLeft(yScale));\n\n  g.selectAll(\"rect\")\n    .data(data)\n    .join(\"rect\")\n    .attr(\"x\", d => xScale(d.category))\n    .attr(\"y\", d => yScale(d.value))\n    .attr(\"width\", xScale.bandwidth())\n    .attr(\"height\", d => innerHeight - yScale(d.value))\n    .attr(\"fill\", \"steelblue\");\n}\n\n\u002F\u002F Usage:\n\u002F\u002F drawBarChart(myData, document.getElementById('chart'));\n```\n\n### Line chart\n\n```javascript\nconst line = d3.line()\n  .x(d => xScale(d.date))\n  .y(d => yScale(d.value))\n  .curve(d3.curveMonotoneX); \u002F\u002F Smooth curve\n\ng.append(\"path\")\n  .datum(data)\n  .attr(\"fill\", \"none\")\n  .attr(\"stroke\", \"steelblue\")\n  .attr(\"stroke-width\", 2)\n  .attr(\"d\", line);\n```\n\n### Scatter plot\n\n```javascript\ng.selectAll(\"circle\")\n  .data(data)\n  .join(\"circle\")\n  .attr(\"cx\", d => xScale(d.x))\n  .attr(\"cy\", d => yScale(d.y))\n  .attr(\"r\", d => sizeScale(d.size)) \u002F\u002F Optional: size encoding\n  .attr(\"fill\", d => colourScale(d.category)) \u002F\u002F Optional: colour encoding\n  .attr(\"opacity\", 0.7);\n```\n\n### Chord diagram\n\nA chord diagram shows relationships between entities in a circular layout, with ribbons representing flows between them:\n\n```javascript\nfunction drawChordDiagram(data) {\n  \u002F\u002F data format: array of objects with source, target, and value\n  \u002F\u002F Example: [{ source: 'A', target: 'B', value: 10 }, ...]\n\n  if (!data || data.length === 0) return;\n\n  const svg = d3.select('#chart');\n  svg.selectAll(\"*\").remove();\n\n  const width = 600;\n  const height = 600;\n  const innerRadius = Math.min(width, height) * 0.3;\n  const outerRadius = innerRadius + 30;\n\n  \u002F\u002F Create matrix from data\n  const nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])));\n  const matrix = Array.from({ length: nodes.length }, () => Array(nodes.length).fill(0));\n\n  data.forEach(d => {\n    const i = nodes.indexOf(d.source);\n    const j = nodes.indexOf(d.target);\n    matrix[i][j] += d.value;\n    matrix[j][i] += d.value;\n  });\n\n  \u002F\u002F Create chord layout\n  const chord = d3.chord()\n    .padAngle(0.05)\n    .sortSubgroups(d3.descending);\n\n  const arc = d3.arc()\n    .innerRadius(innerRadius)\n    .outerRadius(outerRadius);\n\n  const ribbon = d3.ribbon()\n    .source(d => d.source)\n    .target(d => d.target);\n\n  const colourScale = d3.scaleOrdinal(d3.schemeCategory10)\n    .domain(nodes);\n\n  const g = svg.append(\"g\")\n    .attr(\"transform\", `translate(${width \u002F 2},${height \u002F 2})`);\n\n  const chords = chord(matrix);\n\n  \u002F\u002F Draw ribbons\n  g.append(\"g\")\n    .attr(\"fill-opacity\", 0.67)\n    .selectAll(\"path\")\n    .data(chords)\n    .join(\"path\")\n    .attr(\"d\", ribbon)\n    .attr(\"fill\", d => colourScale(nodes[d.source.index]))\n    .attr(\"stroke\", d => d3.rgb(colourScale(nodes[d.source.index])).darker());\n\n  \u002F\u002F Draw groups (arcs)\n  const group = g.append(\"g\")\n    .selectAll(\"g\")\n    .data(chords.groups)\n    .join(\"g\");\n\n  group.append(\"path\")\n    .attr(\"d\", arc)\n    .attr(\"fill\", d => colourScale(nodes[d.index]))\n    .attr(\"stroke\", d => d3.rgb(colourScale(nodes[d.index])).darker());\n\n  \u002F\u002F Add labels\n  group.append(\"text\")\n    .each(d => { d.angle = (d.startAngle + d.endAngle) \u002F 2; })\n    .attr(\"dy\", \"0.31em\")\n    .attr(\"transform\", d => `rotate(${(d.angle * 180 \u002F Math.PI) - 90})translate(${outerRadius + 30})${d.angle > Math.PI ? \"rotate(180)\" : \"\"}`)\n    .attr(\"text-anchor\", d => d.angle > Math.PI ? \"end\" : null)\n    .text((d, i) => nodes[i])\n    .style(\"font-size\", \"12px\");\n}\n```\n\n### Heatmap\n\nA heatmap uses colour to encode values in a two-dimensional grid, useful for showing patterns across categories:\n\n```javascript\nfunction drawHeatmap(data) {\n  \u002F\u002F data format: array of objects with row, column, and value\n  \u002F\u002F Example: [{ row: 'A', column: 'X', value: 10 }, ...]\n\n  if (!data || data.length === 0) return;\n\n  const svg = d3.select('#chart');\n  svg.selectAll(\"*\").remove();\n\n  const width = 800;\n  const height = 600;\n  const margin = { top: 100, right: 30, bottom: 30, left: 100 };\n  const innerWidth = width - margin.left - margin.right;\n  const innerHeight = height - margin.top - margin.bottom;\n\n  \u002F\u002F Get unique rows and columns\n  const rows = Array.from(new Set(data.map(d => d.row)));\n  const columns = Array.from(new Set(data.map(d => d.column)));\n\n  const g = svg.append(\"g\")\n    .attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n  \u002F\u002F Create scales\n  const xScale = d3.scaleBand()\n    .domain(columns)\n    .range([0, innerWidth])\n    .padding(0.01);\n\n  const yScale = d3.scaleBand()\n    .domain(rows)\n    .range([0, innerHeight])\n    .padding(0.01);\n\n  \u002F\u002F Colour scale for values\n  const colourScale = d3.scaleSequential(d3.interpolateYlOrRd)\n    .domain([0, d3.max(data, d => d.value)]);\n\n  \u002F\u002F Draw rectangles\n  g.selectAll(\"rect\")\n    .data(data)\n    .join(\"rect\")\n    .attr(\"x\", d => xScale(d.column))\n    .attr(\"y\", d => yScale(d.row))\n    .attr(\"width\", xScale.bandwidth())\n    .attr(\"height\", yScale.bandwidth())\n    .attr(\"fill\", d => colourScale(d.value));\n\n  \u002F\u002F Add x-axis labels\n  svg.append(\"g\")\n    .attr(\"transform\", `translate(${margin.left},${margin.top})`)\n    .selectAll(\"text\")\n    .data(columns)\n    .join(\"text\")\n    .attr(\"x\", d => xScale(d) + xScale.bandwidth() \u002F 2)\n    .attr(\"y\", -10)\n    .attr(\"text-anchor\", \"middle\")\n    .text(d => d)\n    .style(\"font-size\", \"12px\");\n\n  \u002F\u002F Add y-axis labels\n  svg.append(\"g\")\n    .attr(\"transform\", `translate(${margin.left},${margin.top})`)\n    .selectAll(\"text\")\n    .data(rows)\n    .join(\"text\")\n    .attr(\"x\", -10)\n    .attr(\"y\", d => yScale(d) + yScale.bandwidth() \u002F 2)\n    .attr(\"dy\", \"0.35em\")\n    .attr(\"text-anchor\", \"end\")\n    .text(d => d)\n    .style(\"font-size\", \"12px\");\n\n  \u002F\u002F Add colour legend\n  const legendWidth = 20;\n  const legendHeight = 200;\n  const legend = svg.append(\"g\")\n    .attr(\"transform\", `translate(${width - 60},${margin.top})`);\n\n  const legendScale = d3.scaleLinear()\n    .domain(colourScale.domain())\n    .range([legendHeight, 0]);\n\n  const legendAxis = d3.axisRight(legendScale)\n    .ticks(5);\n\n  \u002F\u002F Draw colour gradient in legend\n  for (let i = 0; i \u003C legendHeight; i++) {\n    legend.append(\"rect\")\n      .attr(\"y\", i)\n      .attr(\"width\", legendWidth)\n      .attr(\"height\", 1)\n      .attr(\"fill\", colourScale(legendScale.invert(i)));\n  }\n\n  legend.append(\"g\")\n    .attr(\"transform\", `translate(${legendWidth},0)`)\n    .call(legendAxis);\n}\n```\n\n### Pie chart\n\n```javascript\nconst pie = d3.pie()\n  .value(d => d.value)\n  .sort(null);\n\nconst arc = d3.arc()\n  .innerRadius(0)\n  .outerRadius(Math.min(width, height) \u002F 2 - 20);\n\nconst colourScale = d3.scaleOrdinal(d3.schemeCategory10);\n\nconst g = svg.append(\"g\")\n  .attr(\"transform\", `translate(${width \u002F 2},${height \u002F 2})`);\n\ng.selectAll(\"path\")\n  .data(pie(data))\n  .join(\"path\")\n  .attr(\"d\", arc)\n  .attr(\"fill\", (d, i) => colourScale(i))\n  .attr(\"stroke\", \"white\")\n  .attr(\"stroke-width\", 2);\n```\n\n### Force-directed network\n\n```javascript\nconst simulation = d3.forceSimulation(nodes)\n  .force(\"link\", d3.forceLink(links).id(d => d.id).distance(100))\n  .force(\"charge\", d3.forceManyBody().strength(-300))\n  .force(\"center\", d3.forceCenter(width \u002F 2, height \u002F 2));\n\nconst link = g.selectAll(\"line\")\n  .data(links)\n  .join(\"line\")\n  .attr(\"stroke\", \"#999\")\n  .attr(\"stroke-width\", 1);\n\nconst node = g.selectAll(\"circle\")\n  .data(nodes)\n  .join(\"circle\")\n  .attr(\"r\", 8)\n  .attr(\"fill\", \"steelblue\")\n  .call(d3.drag()\n    .on(\"start\", dragstarted)\n    .on(\"drag\", dragged)\n    .on(\"end\", dragended));\n\nsimulation.on(\"tick\", () => {\n  link\n    .attr(\"x1\", d => d.source.x)\n    .attr(\"y1\", d => d.source.y)\n    .attr(\"x2\", d => d.target.x)\n    .attr(\"y2\", d => d.target.y);\n  \n  node\n    .attr(\"cx\", d => d.x)\n    .attr(\"cy\", d => d.y);\n});\n\nfunction dragstarted(event) {\n  if (!event.active) simulation.alphaTarget(0.3).restart();\n  event.subject.fx = event.subject.x;\n  event.subject.fy = event.subject.y;\n}\n\nfunction dragged(event) {\n  event.subject.fx = event.x;\n  event.subject.fy = event.y;\n}\n\nfunction dragended(event) {\n  if (!event.active) simulation.alphaTarget(0);\n  event.subject.fx = null;\n  event.subject.fy = null;\n}\n```\n\n## Adding interactivity\n\n### Tooltips\n\n```javascript\n\u002F\u002F Create tooltip div (outside SVG)\nconst tooltip = d3.select(\"body\").append(\"div\")\n  .attr(\"class\", \"tooltip\")\n  .style(\"position\", \"absolute\")\n  .style(\"visibility\", \"hidden\")\n  .style(\"background-color\", \"white\")\n  .style(\"border\", \"1px solid #ddd\")\n  .style(\"padding\", \"10px\")\n  .style(\"border-radius\", \"4px\")\n  .style(\"pointer-events\", \"none\");\n\n\u002F\u002F Add to elements\ncircles\n  .on(\"mouseover\", function(event, d) {\n    d3.select(this).attr(\"opacity\", 1);\n    tooltip\n      .style(\"visibility\", \"visible\")\n      .html(`\u003Cstrong>${d.label}\u003C\u002Fstrong>\u003Cbr\u002F>Value: ${d.value}`);\n  })\n  .on(\"mousemove\", function(event) {\n    tooltip\n      .style(\"top\", (event.pageY - 10) + \"px\")\n      .style(\"left\", (event.pageX + 10) + \"px\");\n  })\n  .on(\"mouseout\", function() {\n    d3.select(this).attr(\"opacity\", 0.7);\n    tooltip.style(\"visibility\", \"hidden\");\n  });\n```\n\n### Zoom and pan\n\n```javascript\nconst zoom = d3.zoom()\n  .scaleExtent([0.5, 10])\n  .on(\"zoom\", (event) => {\n    g.attr(\"transform\", event.transform);\n  });\n\nsvg.call(zoom);\n```\n\n### Click interactions\n\n```javascript\ncircles\n  .on(\"click\", function(event, d) {\n    \u002F\u002F Handle click (dispatch event, update app state, etc.)\n    console.log(\"Clicked:\", d);\n\n    \u002F\u002F Visual feedback\n    d3.selectAll(\"circle\").attr(\"fill\", \"steelblue\");\n    d3.select(this).attr(\"fill\", \"orange\");\n\n    \u002F\u002F Optional: dispatch custom event for your framework\u002Fapp to listen to\n    \u002F\u002F window.dispatchEvent(new CustomEvent('chartClick', { detail: d }));\n  });\n```\n\n## Transitions and animations\n\nAdd smooth transitions to visual changes:\n\n```javascript\n\u002F\u002F Basic transition\ncircles\n  .transition()\n  .duration(750)\n  .attr(\"r\", 10);\n\n\u002F\u002F Chained transitions\ncircles\n  .transition()\n  .duration(500)\n  .attr(\"fill\", \"orange\")\n  .transition()\n  .duration(500)\n  .attr(\"r\", 15);\n\n\u002F\u002F Staggered transitions\ncircles\n  .transition()\n  .delay((d, i) => i * 50)\n  .duration(500)\n  .attr(\"cy\", d => yScale(d.value));\n\n\u002F\u002F Custom easing\ncircles\n  .transition()\n  .duration(1000)\n  .ease(d3.easeBounceOut)\n  .attr(\"r\", 10);\n```\n\n## Scales reference\n\n### Quantitative scales\n\n```javascript\n\u002F\u002F Linear scale\nconst xScale = d3.scaleLinear()\n  .domain([0, 100])\n  .range([0, 500]);\n\n\u002F\u002F Log scale (for exponential data)\nconst logScale = d3.scaleLog()\n  .domain([1, 1000])\n  .range([0, 500]);\n\n\u002F\u002F Power scale\nconst powScale = d3.scalePow()\n  .exponent(2)\n  .domain([0, 100])\n  .range([0, 500]);\n\n\u002F\u002F Time scale\nconst timeScale = d3.scaleTime()\n  .domain([new Date(2020, 0, 1), new Date(2024, 0, 1)])\n  .range([0, 500]);\n```\n\n### Ordinal scales\n\n```javascript\n\u002F\u002F Band scale (for bar charts)\nconst bandScale = d3.scaleBand()\n  .domain(['A', 'B', 'C', 'D'])\n  .range([0, 400])\n  .padding(0.1);\n\n\u002F\u002F Point scale (for line\u002Fscatter categories)\nconst pointScale = d3.scalePoint()\n  .domain(['A', 'B', 'C', 'D'])\n  .range([0, 400]);\n\n\u002F\u002F Ordinal scale (for colours)\nconst colourScale = d3.scaleOrdinal(d3.schemeCategory10);\n```\n\n### Sequential scales\n\n```javascript\n\u002F\u002F Sequential colour scale\nconst colourScale = d3.scaleSequential(d3.interpolateBlues)\n  .domain([0, 100]);\n\n\u002F\u002F Diverging colour scale\nconst divScale = d3.scaleDiverging(d3.interpolateRdBu)\n  .domain([-10, 0, 10]);\n```\n\n## Best practices\n\n### Data preparation\n\nAlways validate and prepare data before visualisation:\n\n```javascript\n\u002F\u002F Filter invalid values\nconst cleanData = data.filter(d => d.value != null && !isNaN(d.value));\n\n\u002F\u002F Sort data if order matters\nconst sortedData = [...data].sort((a, b) => b.value - a.value);\n\n\u002F\u002F Parse dates\nconst parsedData = data.map(d => ({\n  ...d,\n  date: d3.timeParse(\"%Y-%m-%d\")(d.date)\n}));\n```\n\n### Performance optimisation\n\nFor large datasets (>1000 elements):\n\n```javascript\n\u002F\u002F Use canvas instead of SVG for many elements\n\u002F\u002F Use quadtree for collision detection\n\u002F\u002F Simplify paths with d3.line().curve(d3.curveStep)\n\u002F\u002F Implement virtual scrolling for large lists\n\u002F\u002F Use requestAnimationFrame for custom animations\n```\n\n### Accessibility\n\nMake visualisations accessible:\n\n```javascript\n\u002F\u002F Add ARIA labels\nsvg.attr(\"role\", \"img\")\n   .attr(\"aria-label\", \"Bar chart showing quarterly revenue\");\n\n\u002F\u002F Add title and description\nsvg.append(\"title\").text(\"Quarterly Revenue 2024\");\nsvg.append(\"desc\").text(\"Bar chart showing revenue growth across four quarters\");\n\n\u002F\u002F Ensure sufficient colour contrast\n\u002F\u002F Provide keyboard navigation for interactive elements\n\u002F\u002F Include data table alternative\n```\n\n### Styling\n\nUse consistent, professional styling:\n\n```javascript\n\u002F\u002F Define colour palettes upfront\nconst colours = {\n  primary: '#4A90E2',\n  secondary: '#7B68EE',\n  background: '#F5F7FA',\n  text: '#333333',\n  gridLines: '#E0E0E0'\n};\n\n\u002F\u002F Apply consistent typography\nsvg.selectAll(\"text\")\n  .style(\"font-family\", \"Inter, sans-serif\")\n  .style(\"font-size\", \"12px\");\n\n\u002F\u002F Use subtle grid lines\ng.selectAll(\".tick line\")\n  .attr(\"stroke\", colours.gridLines)\n  .attr(\"stroke-dasharray\", \"2,2\");\n```\n\n## Common issues and solutions\n\n**Issue**: Axes not appearing\n- Ensure scales have valid domains (check for NaN values)\n- Verify axis is appended to correct group\n- Check transform translations are correct\n\n**Issue**: Transitions not working\n- Call `.transition()` before attribute changes\n- Ensure elements have unique keys for proper data binding\n- Check that useEffect dependencies include all changing data\n\n**Issue**: Responsive sizing not working\n- Use ResizeObserver or window resize listener\n- Update dimensions in state to trigger re-render\n- Ensure SVG has width\u002Fheight attributes or viewBox\n\n**Issue**: Performance problems\n- Limit number of DOM elements (consider canvas for >1000 items)\n- Debounce resize handlers\n- Use `.join()` instead of separate enter\u002Fupdate\u002Fexit selections\n- Avoid unnecessary re-renders by checking dependencies\n\n## Resources\n\n### references\u002F\nContains detailed reference materials:\n- `d3-patterns.md` - Comprehensive collection of visualisation patterns and code examples\n- `scale-reference.md` - Complete guide to d3 scales with examples\n- `colour-schemes.md` - D3 colour schemes and palette recommendations\n\n### assets\u002F\n\nContains boilerplate templates:\n\n- `chart-template.js` - Starter template for basic chart\n- `interactive-template.js` - Template with tooltips, zoom, and interactions\n- `sample-data.json` - Example datasets for testing\n\nThese templates work with vanilla JavaScript, React, Vue, Svelte, or any other JavaScript environment. Adapt them as needed for your specific framework.\n\nTo use these resources, read the relevant files when detailed guidance is needed for specific visualisation types or patterns.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,210,328,"2026-05-16 13:10:58",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"效率工具","productivity","mdi-lightning-bolt-outline","文档处理、数据分析、自动化工作流",4,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"数据分析","data-analysis","mdi-chart-bar","数据可视化、统计分析",2,30,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"f10fe020-3241-4eeb-afcf-8df419af9736","1.0.0","claude-d3js-skill.zip",24189,"uploads\u002Fskills\u002F220dbcc6-2240-4723-9b3a-2a6054bc5d42\u002Fclaude-d3js-skill.zip","57bbfc2a08ff1040c261ffff37343f5d7ed8b175c7486659ea2c336e4e7c2aef","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":21813},{\"path\":\"assets\u002Fchart-template.jsx\",\"isDirectory\":false,\"size\":2820},{\"path\":\"assets\u002Finteractive-template.jsx\",\"isDirectory\":false,\"size\":6468},{\"path\":\"assets\u002Fsample-data.json\",\"isDirectory\":false,\"size\":4480},{\"path\":\"references\u002Fcolour-schemes.md\",\"isDirectory\":false,\"size\":12761},{\"path\":\"references\u002Fd3-patterns.md\",\"isDirectory\":false,\"size\":21605},{\"path\":\"references\u002Fscale-reference.md\",\"isDirectory\":false,\"size\":12079}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]