{"spec_id":"bar-error","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// bar-error: Bar Chart with Error Bars\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ---------------------------------------\n// Plant height (cm) by fertilizer treatment, error bars show +/-1 SD.\nconst treatments = [\"Control\", \"Nitrogen\", \"Phosphorus\", \"Potassium\", \"N+P\", \"NPK Combined\"];\nconst heights = [24.5, 31.2, 27.8, 26.1, 34.6, 39.8];\nconst stdDevs = [2.1, 2.8, 2.3, 2.0, 3.1, 3.4];\n\nconst maxWithError = Math.max(...heights.map((h, i) => h + stdDevs[i]));\n\n// --- Error bar plugin (native Chart.js plugin API, drawn on canvas) --------\nconst errorBarsPlugin = {\n  id: \"errorBars\",\n  afterDatasetsDraw(chart) {\n    const { ctx, scales: { y } } = chart;\n    const meta = chart.getDatasetMeta(0);\n    const capWidth = 16;\n\n    ctx.save();\n    ctx.strokeStyle = t.ink;\n    ctx.lineWidth = 2.5;\n    ctx.lineCap = \"round\";\n\n    meta.data.forEach((bar, i) => {\n      const xCenter = bar.x;\n      const yTop = y.getPixelForValue(heights[i] + stdDevs[i]);\n      const yBottom = y.getPixelForValue(heights[i] - stdDevs[i]);\n\n      ctx.beginPath();\n      ctx.moveTo(xCenter, yTop);\n      ctx.lineTo(xCenter, yBottom);\n      ctx.stroke();\n\n      ctx.beginPath();\n      ctx.moveTo(xCenter - capWidth / 2, yTop);\n      ctx.lineTo(xCenter + capWidth / 2, yTop);\n      ctx.stroke();\n\n      ctx.beginPath();\n      ctx.moveTo(xCenter - capWidth / 2, yBottom);\n      ctx.lineTo(xCenter + capWidth / 2, yBottom);\n      ctx.stroke();\n    });\n\n    ctx.restore();\n  },\n};\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart -------------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"bar\",\n  data: {\n    labels: treatments,\n    datasets: [\n      {\n        label: \"Mean Plant Height\",\n        data: heights,\n        backgroundColor: t.palette[0],\n        borderWidth: 0,\n        barPercentage: 0.6,\n      },\n    ],\n  },\n  plugins: [errorBarsPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 20 } },\n    plugins: {\n      title: {\n        display: true,\n        text: \"bar-error · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22, weight: \"500\" },\n        padding: { bottom: 8 },\n      },\n      subtitle: {\n        display: true,\n        text: \"Error bars show ±1 standard deviation across replicate plots\",\n        color: t.inkSoft,\n        font: { size: 16 },\n        padding: { bottom: 20 },\n      },\n      legend: { display: false },\n    },\n    scales: {\n      x: {\n        title: { display: true, text: \"Fertilizer Treatment\", color: t.ink, font: { size: 18 } },\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n      },\n      y: {\n        beginAtZero: true,\n        suggestedMax: Math.ceil(maxWithError * 1.1),\n        title: { display: true, text: \"Plant Height (cm)\", color: t.ink, font: { size: 18 } },\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n      },\n    },\n  },\n});\n"}