{"spec_id":"slope-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// slope-basic: Basic Slope Chart (Slopegraph)\n// Library: chartjs 4.4.7 | JavaScript 22.23.1\n// Quality: 93/100 | Created: 2026-07-25\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Reading tutoring program: student scores (out of 100) before and after.\nconst students = [\n  \"Ava\", \"Liam\", \"Sophia\", \"Mason\", \"Isabella\",\n  \"Ethan\", \"Mia\", \"Noah\", \"Amelia\", \"Lucas\",\n];\nconst preTest = [62, 58, 71, 45, 80, 55, 67, 73, 49, 61];\nconst postTest = [78, 65, 74, 52, 88, 70, 66, 90, 61, 59];\n\nconst INCREASE = t.palette[0]; // #009E73 brand green — up/gain (semantic exception)\nconst DECREASE = \"#AE3030\"; // matte red — down/loss (semantic anchor, slot 5)\n\nconst datasets = students.map((name, i) => {\n  const color = postTest[i] >= preTest[i] ? INCREASE : DECREASE;\n  return {\n    label: name,\n    data: [preTest[i], postTest[i]],\n    borderColor: color,\n    backgroundColor: color,\n    pointBackgroundColor: color,\n    pointBorderColor: color,\n    pointRadius: 6,\n    pointHoverRadius: 6,\n    borderWidth: 3,\n    tension: 0,\n  };\n});\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Endpoint label plugin (native Chart.js plugin API, no external deps) ---\n// Draws entity name + value at both ends of each line, matching the spec's\n// \"labels at both endpoints\" requirement while keeping the legend uncluttered.\nconst endpointLabels = {\n  id: \"slopeEndpointLabels\",\n  afterDatasetsDraw(chart) {\n    const { ctx } = chart;\n    ctx.save();\n    ctx.font = \"600 15px sans-serif\";\n    ctx.textBaseline = \"middle\";\n    chart.data.datasets.forEach((ds, i) => {\n      const meta = chart.getDatasetMeta(i);\n      if (meta.hidden) return;\n      const [p0, p1] = meta.data;\n      ctx.fillStyle = ds.borderColor;\n      ctx.textAlign = \"right\";\n      ctx.fillText(`${ds.label}  ${ds.data[0]}`, p0.x - 14, p0.y);\n      ctx.textAlign = \"left\";\n      ctx.fillText(`${ds.data[1]}  ${ds.label}`, p1.x + 14, p1.y);\n    });\n    ctx.restore();\n  },\n};\nChart.register(endpointLabels);\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"line\",\n  data: {\n    labels: [\"Pre-Test\", \"Post-Test\"],\n    datasets,\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: {\n      padding: { left: 190, right: 190, top: 20, bottom: 10 },\n    },\n    plugins: {\n      title: {\n        display: true,\n        text: \"slope-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n      },\n      legend: {\n        labels: {\n          color: t.ink,\n          font: { size: 16 },\n          generateLabels: () => [\n            { text: \"Increase\", fillStyle: INCREASE, strokeStyle: INCREASE, lineWidth: 0 },\n            { text: \"Decrease\", fillStyle: DECREASE, strokeStyle: DECREASE, lineWidth: 0 },\n          ],\n        },\n        onClick: () => {},\n      },\n      tooltip: { enabled: false },\n    },\n    scales: {\n      x: {\n        type: \"category\",\n        offset: false,\n        ticks: { color: t.inkSoft, font: { size: 18, weight: \"600\" } },\n        grid: { color: t.grid, drawTicks: false },\n        border: { color: t.grid },\n      },\n      y: {\n        display: false,\n      },\n    },\n  },\n});\n"}