{"spec_id":"count-basic","library":"d3","language":"javascript","code":"// anyplot.ai\n// count-basic: Basic Count Plot\n// Library: d3 7.9.0 | JavaScript 22.23.2\n// Quality: 88/100 | Updated: 2026-09-30\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\nconst margin = { top: 170, right: 60, bottom: 120, left: 130 };\nconst iw = width - margin.left - margin.right;\nconst ih = height - margin.top - margin.bottom;\n\n// --- Data: raw per-respondent observations, counted (not pre-aggregated) ---\n// Small LCG so results are deterministic without a browser RNG.\nconst lcg = (seed) => {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n};\nconst random = lcg(42);\n\nconst languageWeights = [\n  { language: \"Python\", weight: 28 },\n  { language: \"JavaScript\", weight: 24 },\n  { language: \"TypeScript\", weight: 16 },\n  { language: \"Java\", weight: 12 },\n  { language: \"Go\", weight: 9 },\n  { language: \"Rust\", weight: 7 },\n  { language: \"C++\", weight: 4 },\n];\nlet cumulative = 0;\nconst cumulativeWeights = languageWeights.map((d) => {\n  cumulative += d.weight;\n  return { language: d.language, upTo: cumulative };\n});\nconst totalWeight = cumulative;\n\nconst RESPONDENTS = 640;\nconst surveyResponses = Array.from({ length: RESPONDENTS }, () => {\n  const r = random() * totalWeight;\n  return cumulativeWeights.find((d) => r <= d.upTo).language;\n});\n\nconst counts = Array.from(\n  d3.rollup(\n    surveyResponses,\n    (v) => v.length,\n    (d) => d,\n  ),\n  ([language, count]) => ({ language, count }),\n).sort((a, b) => d3.descending(a.count, b.count));\n\n// --- SVG mount ---------------------------------------------------------------\nconst svg = d3.select(\"#container\").append(\"svg\").attr(\"width\", width).attr(\"height\", height);\nconst g = svg.append(\"g\").attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n// --- Scales --------------------------------------------------------------------\nconst x = d3\n  .scaleBand()\n  .domain(counts.map((d) => d.language))\n  .range([0, iw])\n  .padding(0.35);\nconst y = d3\n  .scaleLinear()\n  .domain([0, d3.max(counts, (d) => d.count)])\n  .nice()\n  .range([ih, 0]);\n\n// --- Gridlines (y-axis only, subtle) --------------------------------------------\ng.append(\"g\")\n  .selectAll(\"line\")\n  .data(y.ticks(6))\n  .join(\"line\")\n  .attr(\"x1\", 0)\n  .attr(\"x2\", iw)\n  .attr(\"y1\", (d) => y(d))\n  .attr(\"y2\", (d) => y(d))\n  .attr(\"stroke\", t.grid)\n  .attr(\"stroke-width\", 1);\n\n// --- Axes ------------------------------------------------------------------------\nconst xAxis = g\n  .append(\"g\")\n  .attr(\"transform\", `translate(0,${ih})`)\n  .call(d3.axisBottom(x).tickSize(0).tickPadding(14));\nconst yAxis = g\n  .append(\"g\")\n  .call(d3.axisLeft(y).ticks(6).tickFormat(d3.format(\",d\")).tickSize(0).tickPadding(12));\nfor (const axis of [xAxis, yAxis]) {\n  axis.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"16px\");\n  axis.select(\".domain\").remove();\n}\nyAxis.selectAll(\".tick line\").attr(\"stroke\", t.grid);\n\n// --- Bars ---------------------------------------------------------------------\n// Single Imprint color at full opacity for every bar, so the data color stays\n// identical between themes (no opacity-over-background blending) — the basic\n// variant's single categorical variable, per the spec.\ng.selectAll(\"rect\")\n  .data(counts)\n  .join(\"rect\")\n  .attr(\"x\", (d) => x(d.language))\n  .attr(\"y\", (d) => y(d.count))\n  .attr(\"width\", x.bandwidth())\n  .attr(\"height\", (d) => ih - y(d.count))\n  .attr(\"fill\", t.palette[0])\n  .attr(\"stroke\", t.ink)\n  .attr(\"stroke-opacity\", 0.15)\n  .attr(\"stroke-width\", 1);\n\n// --- Count labels above bars ---------------------------------------------------\ng.selectAll(\".count-label\")\n  .data(counts)\n  .join(\"text\")\n  .attr(\"class\", \"count-label\")\n  .attr(\"x\", (d) => x(d.language) + x.bandwidth() / 2)\n  .attr(\"y\", (d) => y(d.count) - 16)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .style(\"font-weight\", \"600\")\n  .text((d) => d.count);\n\n// --- Axis titles -----------------------------------------------------------------\ng.append(\"text\")\n  .attr(\"x\", iw / 2)\n  .attr(\"y\", ih + 76)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"18px\")\n  .text(\"Primary Programming Language\");\n\ng.append(\"text\")\n  .attr(\"transform\", \"rotate(-90)\")\n  .attr(\"x\", -ih / 2)\n  .attr(\"y\", -92)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"18px\")\n  .text(\"Number of Respondents\");\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\", \"26px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"count-basic · javascript · d3 · anyplot.ai\");\n\nsvg\n  .append(\"text\")\n  .attr(\"x\", width / 2)\n  .attr(\"y\", 88)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"17px\")\n  .text(`Developer survey · n = ${RESPONDENTS} respondents · sorted by frequency`);\n"}