{"spec_id":"gantt-basic","library":"d3","language":"javascript","code":"// anyplot.ai\n// gantt-basic: Basic Gantt Chart\n// Library: d3 7.9.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-09-05\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\nconst margin = { top: 150, right: 70, bottom: 70, left: 270 };\nconst iw = width - margin.left - margin.right;\nconst ih = height - margin.top - margin.bottom;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Website redesign project schedule, ordered by start date (earliest on top).\nconst tasks = [\n  { task: \"Requirements Gathering\", start: new Date(2026, 0, 5), end: new Date(2026, 0, 16), category: \"Planning\" },\n  { task: \"Stakeholder Interviews\", start: new Date(2026, 0, 12), end: new Date(2026, 0, 23), category: \"Planning\" },\n  { task: \"Budget Approval\", start: new Date(2026, 0, 19), end: new Date(2026, 0, 26), category: \"Planning\" },\n  { task: \"Wireframe Design\", start: new Date(2026, 0, 26), end: new Date(2026, 1, 13), category: \"Design\" },\n  { task: \"Visual Design\", start: new Date(2026, 1, 9), end: new Date(2026, 1, 27), category: \"Design\" },\n  { task: \"Design Review\", start: new Date(2026, 1, 23), end: new Date(2026, 1, 27), category: \"Design\" },\n  { task: \"Frontend Development\", start: new Date(2026, 1, 2), end: new Date(2026, 3, 10), category: \"Development\" },\n  { task: \"Backend Development\", start: new Date(2026, 1, 2), end: new Date(2026, 3, 24), category: \"Development\" },\n  { task: \"API Integration\", start: new Date(2026, 3, 6), end: new Date(2026, 4, 1), category: \"Development\" },\n  { task: \"Unit Testing\", start: new Date(2026, 3, 20), end: new Date(2026, 4, 7), category: \"Testing\" },\n  { task: \"Integration Testing\", start: new Date(2026, 4, 4), end: new Date(2026, 4, 22), category: \"Testing\" },\n  { task: \"User Acceptance Testing\", start: new Date(2026, 4, 18), end: new Date(2026, 4, 29), category: \"Testing\" },\n  { task: \"Production Launch\", start: new Date(2026, 5, 1), end: new Date(2026, 5, 5), category: \"Testing\" },\n];\n\nconst categories = [\"Planning\", \"Design\", \"Development\", \"Testing\"];\nconst color = d3.scaleOrdinal().domain(categories).range(t.palette.slice(0, categories.length));\nconst referenceDate = new Date(2026, 2, 16);\n\n// --- Scales ------------------------------------------------------------------\nconst x = d3\n  .scaleTime()\n  .domain([d3.min(tasks, (d) => d.start), d3.max(tasks, (d) => d.end)])\n  .nice(d3.timeMonth)\n  .range([0, iw]);\n\nconst y = d3\n  .scaleBand()\n  .domain(tasks.map((d) => d.task))\n  .range([0, ih])\n  .padding(0.35);\n\n// --- SVG mount ---------------------------------------------------------------\nconst svg = d3.select(\"#container\").append(\"svg\").attr(\"width\", width).attr(\"height\", height);\n\n// --- Title ---------------------------------------------------------------------\nsvg\n  .append(\"text\")\n  .attr(\"x\", width / 2)\n  .attr(\"y\", 54)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"24px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"gantt-basic · javascript · d3 · anyplot.ai\");\n\n// --- Legend (category color key) ---------------------------------------------\nconst legend = svg.append(\"g\").attr(\"transform\", `translate(${width - margin.right - 640},96)`);\nconst legendItem = legend\n  .selectAll(\"g\")\n  .data(categories)\n  .join(\"g\")\n  .attr(\"transform\", (_d, i) => `translate(${i * 160},0)`);\nlegendItem\n  .append(\"rect\")\n  .attr(\"width\", 16)\n  .attr(\"height\", 16)\n  .attr(\"rx\", 3)\n  .attr(\"fill\", (d) => color(d));\nlegendItem\n  .append(\"text\")\n  .attr(\"x\", 24)\n  .attr(\"y\", 13)\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"15px\")\n  .text((d) => d);\n\nconst g = svg.append(\"g\").attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n// --- Row banding (alternating rows aid scanning across many tasks) -----------\ng.selectAll(\".row-band\")\n  .data(tasks)\n  .join(\"rect\")\n  .attr(\"class\", \"row-band\")\n  .attr(\"x\", 0)\n  .attr(\"y\", (d) => y(d.task))\n  .attr(\"width\", iw)\n  .attr(\"height\", y.bandwidth() / (1 - 0.35))\n  .attr(\"fill\", t.elevatedBg)\n  .attr(\"opacity\", (_d, i) => (i % 2 === 0 ? 0.5 : 0));\n\n// --- Vertical gridlines (aligned to x-axis month ticks) -----------------------\ng.append(\"g\")\n  .attr(\"class\", \"grid\")\n  .selectAll(\"line\")\n  .data(x.ticks(d3.timeMonth.every(1)))\n  .join(\"line\")\n  .attr(\"x1\", (d) => x(d))\n  .attr(\"x2\", (d) => x(d))\n  .attr(\"y1\", 0)\n  .attr(\"y2\", ih)\n  .attr(\"stroke\", t.grid);\n\n// --- Axes ----------------------------------------------------------------------\nconst xAxis = g\n  .append(\"g\")\n  .attr(\"transform\", `translate(0,${ih})`)\n  .call(d3.axisBottom(x).ticks(d3.timeMonth.every(1)).tickFormat(d3.timeFormat(\"%b %d\")).tickSizeOuter(0));\nxAxis.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\");\nxAxis.selectAll(\"line\").attr(\"stroke\", t.grid);\nxAxis.select(\".domain\").attr(\"stroke\", t.inkSoft);\n\nconst yAxis = g.append(\"g\").call(d3.axisLeft(y).tickSize(0));\nyAxis.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\");\nyAxis.select(\".domain\").remove();\n\n// --- Reference line (current-date marker) -------------------------------------\ng.append(\"line\")\n  .attr(\"x1\", x(referenceDate))\n  .attr(\"x2\", x(referenceDate))\n  .attr(\"y1\", -12)\n  .attr(\"y2\", ih)\n  .attr(\"stroke\", t.ink)\n  .attr(\"stroke-width\", 2)\n  .attr(\"stroke-dasharray\", \"6,4\");\ng.append(\"text\")\n  .attr(\"x\", x(referenceDate))\n  .attr(\"y\", -18)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"13px\")\n  .text(\"Today\");\n\n// --- Task bars -------------------------------------------------------------\ng.selectAll(\".task-bar\")\n  .data(tasks)\n  .join(\"rect\")\n  .attr(\"class\", \"task-bar\")\n  .attr(\"x\", (d) => x(d.start))\n  .attr(\"y\", (d) => y(d.task))\n  .attr(\"width\", (d) => Math.max(2, x(d.end) - x(d.start)))\n  .attr(\"height\", y.bandwidth())\n  .attr(\"rx\", 4)\n  .attr(\"fill\", (d) => color(d.category));\n"}