Use Cases (Mis à jour: 02/06/2026)

Guide D3.js avec Claude Code pour la visualisation de données

Créez des graphiques D3.js avec Claude Code : TypeScript exécutable, cas d'usage, pièges, vérification et CTA.

Guide D3.js avec Claude Code pour la visualisation de données

Pourquoi utiliser Claude Code avec D3.js

Avec D3.js, la difficulté n’est pas seulement de dessiner une première barre. Elle arrive quand il faut comprendre comment scaleBand, axisLeft, selection.join et transition fonctionnent ensemble, puis garder le graphique fiable avec de vraies données, du responsive, de l’accessibilité et des changements produit.

Claude Code aide parce qu’il peut lire le HTML, le CSS, le TypeScript, les contrats de données et les conventions du projet avant de produire le code. D3.js relie les données au DOM et au SVG ; une consigne floue donne souvent un graphique qui marche une fois, mais qui devient fragile.

Gardez ces liens officiels à portée de main : D3 Getting started, Joining data, d3-scale, d3-axis et d3-transition.

Modèle mental pour débuter

D3 transforme des données en pixels et en éléments SVG. Nommer chaque étape rend les prompts plus précis.

ÉlémentExplication simpleRôle dans l’exemple
selectionSélectionner des éléments du DOMAjouter un svg dans #chart
scaleConvertir une valeur en position écranPlacer les canaux sur x et les conversions sur y
axisDessiner graduations et libellésAfficher canaux et nombres de conversion
markForme visibleBarres rect et ligne path
joinAssocier lignes de données et nœuds DOMMettre à jour les barres quand les données changent
transitionAnimer un changementFaire pousser les barres depuis la ligne de base

Un prompt utile donne la structure de données, les unités, les états vides, le clavier et la vérification.

Construis un graphique en barres responsive en Vite + TypeScript + D3 v7 pour les visiteurs et conversions par canal. Utilise selection.join, tooltip, focus clavier, aria-label, état sans données et test rapide dans la console.

Exemple D3.js + TypeScript exécutable

Créez l’app avec npm create vite@latest d3-demo -- --template vanilla-ts, installez npm i d3 @types/d3, remplacez ces fichiers, puis lancez npm run dev.

{
  "scripts": {
    "dev": "vite"
  },
  "dependencies": {
    "d3": "^7.9.0"
  },
  "devDependencies": {
    "@types/d3": "^7.4.3",
    "typescript": "latest",
    "vite": "latest"
  }
}
<!doctype html>
<html lang="fr">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>D3 Conversion Chart</title>
  </head>
  <body>
    <main class="page">
      <h1>D3.js Conversion Dashboard</h1>
      <section class="chart-shell" aria-describedby="chart-summary">
        <div id="chart"></div>
        <p id="chart-summary" class="sr-only"></p>
      </section>
    </main>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
:root {
  color: #172033;
  background: #f7f7f3;
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

body {
  margin: 0;
}

.page {
  width: min(920px, calc(100vw - 32px));
  margin: 40px auto;
}

.chart-shell {
  border: 1px solid #d8d5cc;
  border-radius: 8px;
  background: #ffffff;
  padding: 20px;
}

#chart {
  min-height: 320px;
  position: relative;
}

#chart svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.axis-label {
  fill: #475569;
  font-size: 12px;
}

.bar {
  fill: #2563eb;
  outline: none;
}

.bar:hover,
.bar:focus {
  fill: #dc2626;
}

.trend-line {
  fill: none;
  stroke: #0f172a;
  stroke-width: 2;
  pointer-events: none;
}

.chart-tooltip {
  position: absolute;
  top: 0;
  left: 0;
  max-width: 220px;
  border-radius: 6px;
  background: #172033;
  color: #ffffff;
  font-size: 13px;
  line-height: 1.5;
  opacity: 0;
  padding: 8px 10px;
  pointer-events: none;
  transform: translate(-9999px, -9999px);
  transition: opacity 120ms ease;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}
import * as d3 from "d3";
import "./style.css";

type ChannelDatum = {
  channel: string;
  visitors: number;
  conversions: number;
};

const data: ChannelDatum[] = [
  { channel: "Search", visitors: 4200, conversions: 168 },
  { channel: "Newsletter", visitors: 2600, conversions: 182 },
  { channel: "Social", visitors: 3100, conversions: 96 },
  { channel: "Partner", visitors: 1400, conversions: 84 },
];

const numberFormat = new Intl.NumberFormat(undefined);
const percentFormat = new Intl.NumberFormat(undefined, {
  style: "percent",
  maximumFractionDigits: 1,
});

function conversionRate(datum: ChannelDatum): number {
  return datum.visitors === 0 ? 0 : datum.conversions / datum.visitors;
}

function drawConversionChart(container: HTMLElement, items: ChannelDatum[]): void {
  container.replaceChildren();

  if (items.length === 0) {
    container.textContent = "No data to display.";
    return;
  }

  const margin = { top: 28, right: 24, bottom: 56, left: 64 };
  const outerWidth = 760;
  const outerHeight = 420;
  const width = outerWidth - margin.left - margin.right;
  const height = outerHeight - margin.top - margin.bottom;

  const svg = d3
    .select(container)
    .append("svg")
    .attr("viewBox", `0 0 ${outerWidth} ${outerHeight}`)
    .attr("role", "img")
    .attr("aria-labelledby", "chart-title chart-desc");

  svg.append("title").attr("id", "chart-title").text("Conversions by channel");
  svg
    .append("desc")
    .attr("id", "chart-desc")
    .text("Bar chart comparing conversions from each acquisition channel.");

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

  const x = d3
    .scaleBand<string>()
    .domain(items.map((d) => d.channel))
    .range([0, width])
    .padding(0.28);

  const y = d3
    .scaleLinear()
    .domain([0, d3.max(items, (d) => d.conversions) ?? 0])
    .nice()
    .range([height, 0]);

  plot
    .append("g")
    .attr("transform", `translate(0,${height})`)
    .call(d3.axisBottom(x))
    .call((axis) => axis.selectAll("text").attr("dy", "0.85em"));

  plot.append("g").call(d3.axisLeft(y).ticks(5));

  plot
    .append("text")
    .attr("class", "axis-label")
    .attr("x", -margin.left + 4)
    .attr("y", -10)
    .text("Conversions");

  const tooltip = d3.select(container).append("div").attr("class", "chart-tooltip");

  function showTooltip(event: PointerEvent | FocusEvent, datum: ChannelDatum): void {
    const xCenter = (x(datum.channel) ?? 0) + x.bandwidth() / 2 + margin.left;
    const yTop = y(datum.conversions) + margin.top;
    const [left, top] =
      "clientX" in event ? d3.pointer(event, container) : [xCenter, yTop];

    tooltip
      .style("opacity", "1")
      .style("transform", `translate(${left + 12}px, ${top - 28}px)`)
      .html(
        `<strong>${datum.channel}</strong><br />` +
          `Visitors: ${numberFormat.format(datum.visitors)}<br />` +
          `Conversions: ${numberFormat.format(datum.conversions)}<br />` +
          `CVR: ${percentFormat.format(conversionRate(datum))}`,
      );
  }

  function hideTooltip(): void {
    tooltip.style("opacity", "0").style("transform", "translate(-9999px, -9999px)");
  }

  const bars = plot
    .selectAll<SVGRectElement, ChannelDatum>("rect.bar")
    .data(items, (d) => d.channel)
    .join((enter) =>
      enter
        .append("rect")
        .attr("class", "bar")
        .attr("x", (d) => x(d.channel) ?? 0)
        .attr("width", x.bandwidth())
        .attr("y", height)
        .attr("height", 0),
    )
    .attr("tabindex", 0)
    .attr("role", "img")
    .attr(
      "aria-label",
      (d) =>
        `${d.channel}: ${numberFormat.format(d.conversions)} conversions, ${percentFormat.format(
          conversionRate(d),
        )} conversion rate`,
    )
    .on("pointerenter pointermove", showTooltip)
    .on("focus", showTooltip)
    .on("pointerleave blur", hideTooltip);

  bars
    .transition()
    .duration(700)
    .delay((_d, index) => index * 80)
    .attr("x", (d) => x(d.channel) ?? 0)
    .attr("width", x.bandwidth())
    .attr("y", (d) => y(d.conversions))
    .attr("height", (d) => height - y(d.conversions));

  const trendLine = d3
    .line<ChannelDatum>()
    .x((d) => (x(d.channel) ?? 0) + x.bandwidth() / 2)
    .y((d) => y(d.conversions))
    .curve(d3.curveMonotoneX);

  plot
    .append("path")
    .datum(items)
    .attr("class", "trend-line")
    .attr("d", trendLine);
}

const chart = document.querySelector<HTMLElement>("#chart");

if (!chart) {
  throw new Error("Missing #chart element.");
}

drawConversionChart(chart, data);

const summary = document.querySelector<HTMLElement>("#chart-summary");

if (summary) {
  const best = data.reduce((current, item) =>
    conversionRate(item) > conversionRate(current) ? item : current,
  );

  summary.textContent = `Highest conversion rate: ${best.channel}, ${percentFormat.format(
    conversionRate(best),
  )}.`;
}

Vérification rapide dans la console :

console.log(document.querySelectorAll("#chart rect.bar").length);
console.log(document.querySelector("#chart svg")?.getAttribute("role"));
console.log(document.querySelector("#chart-summary")?.textContent);

Cas d’usage concrets

CasPourquoi D3 convientCe que Claude Code doit faire
Funnel de contenuComparer source, catégorie et clic CTAReprendre le plan de mesure analytics
Dashboard SaaSMontrer usage, rétention et écarts par planVerrouiller types, chargement, état vide et segments
Reporting opérationnelMettre en avant seuils et anomaliesVérifier la charge avec performance
Résultats A/B testVisualiser les écarts de conversionSéparer calcul et affichage comme dans A/B testing

La valeur métier vient quand le graphique explique une action liée au revenu : inscription, achat, contact, renouvellement ou activation produit.

Pièges fréquents

PiègeSymptômeCorrection
Redessiner sans nettoyageBarres et axes se dupliquentNettoyer le conteneur ou retourner une fonction cleanup
Ignorer les scalesLe mobile ou les nouvelles valeurs cassent le renduPasser les valeurs par scaleLinear, scaleBand ou une autre scale
Dessiner seulement enterLes anciennes données restent visiblesUtiliser selection.data(...).join(...)
Tooltip uniquement sourisLe clavier n’a pas l’informationAjouter tabindex, aria-label et focus
Conflit avec React ou AstroLe framework écrase le DOM modifié par D3Isoler D3 dans un conteneur
Sens porté seulement par la couleurAccessibilité faibleAjouter libellés, valeurs, légende et contraste ; voir accessibilité

Demandez une revue explicite : doublons au redraw, tableau vide, division par zéro, clavier, lecteurs d’écran, largeur mobile et 1000 points.

SEO, monétisation et vérification

Un article D3.js attire souvent du trafic, mais il faut relier le code à un objectif mesurable et à des liens internes comme TypeScript, SEO et design system.

ClaudeCodeLab propose des supports de configuration Claude Code, des prompts de revue, des modèles CLAUDE.md et du conseil. Pour transformer ce snippet en standard d’équipe, consultez les produits ou la page training et consultation.

Lors de l’essai pratique, le plus gros gain est venu de la demande initiale d’accessibilité et de vérification. Ajouter aria-label, focus clavier, état vide et smoke test dès le premier prompt coûte moins cher que les corriger ensuite.

#Claude Code #D3.js #visualisation de données #graphique #frontend
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.