{"spec_id":"slope-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// slope-basic: Basic Slope Chart (Slopegraph)\n// Library: echarts 6.1.0 | JavaScript 22.23.1\n// Quality: 94/100 | Updated: 2026-07-26\n\n//# anyplot-orientation: square\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ---------------------------------------\n// Employee engagement survey score (0-100) by department, before vs after a\n// company-wide feedback program.\nconst departments = [\n  { name: \"Product\", start: 82, end: 88 },\n  { name: \"HR\", start: 80, end: 83 },\n  { name: \"Marketing\", start: 75, end: 70 },\n  { name: \"Finance\", start: 77, end: 72 },\n  { name: \"Design\", start: 73, end: 77 },\n  { name: \"Engineering\", start: 72, end: 81 },\n  { name: \"Sales\", start: 68, end: 74 },\n  { name: \"Operations\", start: 70, end: 68 },\n  { name: \"Support\", start: 64, end: 79 },\n  { name: \"Legal\", start: 66, end: 65 },\n];\n\nconst INCREASE = t.palette[0]; // \"#009E73\" brand green — profit/up/gain\nconst DECREASE = t.palette[4]; // \"#AE3030\" matte red — semantic anchor for loss/down\n\n// Biggest mover (by absolute change) drives the callout annotation below.\nconst biggestMover = departments.reduce((a, b) =>\n  Math.abs(b.end - b.start) > Math.abs(a.end - a.start) ? b : a,\n);\nconst biggestDelta = biggestMover.end - biggestMover.start;\n\n// --- Init -------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -----------------------------------------------------------------\nconst title = \"Employee Engagement Score · slope-basic · javascript · echarts · anyplot.ai\";\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: title,\n    left: \"center\",\n    top: 30,\n    textStyle: { color: t.ink, fontSize: 20, fontWeight: 500 },\n  },\n  graphic: {\n    type: \"text\",\n    left: \"center\",\n    top: 68,\n    style: {\n      text: `Biggest mover: ${biggestMover.name} ${biggestDelta > 0 ? \"+\" : \"\"}${biggestDelta} pts`,\n      fill: t.inkSoft,\n      fontSize: 14,\n      fontStyle: \"italic\",\n    },\n  },\n  grid: { left: 190, right: 190, top: 130, bottom: 90 },\n  xAxis: {\n    type: \"category\",\n    data: [\"2024 (before)\", \"2025 (after)\"],\n    boundaryGap: false,\n    axisLabel: { color: t.inkSoft, fontSize: 16 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    min: 62,\n    max: 90,\n    show: false,\n  },\n  series: departments.map((d) => {\n    const color = d.end >= d.start ? INCREASE : DECREASE;\n    return {\n      name: d.name,\n      type: \"line\",\n      symbol: \"circle\",\n      symbolSize: 11,\n      lineStyle: { width: 3, color },\n      itemStyle: { color },\n      emphasis: { focus: \"series\", lineStyle: { width: 5 } },\n      blur: { lineStyle: { opacity: 0.15 }, label: { opacity: 0.25 } },\n      data: [\n        {\n          value: d.start,\n          label: {\n            show: true,\n            position: \"left\",\n            distance: 16,\n            formatter: () => `${d.name}  ${d.start}`,\n            color: t.ink,\n            fontSize: 14,\n          },\n        },\n        {\n          value: d.end,\n          label: {\n            show: true,\n            position: \"right\",\n            distance: 16,\n            formatter: () => `${d.end}  ${d.name}`,\n            color: t.ink,\n            fontSize: 14,\n          },\n        },\n      ],\n    };\n  }),\n});\n"}