{"spec_id":"bar-stacked","library":"echarts","language":"javascript","code":"// anyplot.ai\n// bar-stacked: Stacked Bar Chart\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 94/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Monthly software company revenue composition by product line ($K)\nconst months = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"];\nconst components = [\n  { name: \"Subscriptions\", data: [20, 25, 30, 36, 42, 50] },\n  { name: \"Software\", data: [58, 62, 65, 70, 74, 78] },\n  { name: \"Services\", data: [30, 32, 35, 33, 38, 40] },\n  { name: \"Hardware\", data: [42, 45, 40, 48, 44, 50] },\n];\nconst totals = months.map((_, i) =>\n  components.reduce((sum, c) => sum + c.data[i], 0)\n);\nconst avgTotal = Math.round(\n  totals.reduce((a, b) => a + b, 0) / totals.length\n);\nconst subs = components[0].data;\nconst subsGrowthPct = Math.round(\n  ((subs[subs.length - 1] - subs[0]) / subs[0]) * 100\n);\n\n// --- Init --------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Title (fontsize scaled to length; see plot-generator.md formula) -------\nconst titleText =\n  \"Monthly SaaS Revenue by Product Line · bar-stacked · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.round(22 * Math.min(1, 67 / titleText.length));\n\n// --- Option --------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: titleText,\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n  },\n  legend: {\n    top: 70,\n    data: components.map((c) => c.name),\n    textStyle: { color: t.ink, fontSize: 16 },\n  },\n  tooltip: {\n    trigger: \"axis\",\n    axisPointer: { type: \"shadow\" },\n    formatter: (params) => {\n      const idx = params[0].dataIndex;\n      const total = totals[idx];\n      const rows = params\n        .map(\n          (p) =>\n            `${p.marker} ${p.seriesName}: $${p.value}K (${Math.round(\n              (p.value / total) * 100\n            )}%)`\n        )\n        .join(\"<br/>\");\n      return `<strong>${months[idx]}</strong><br/>${rows}<br/><strong>Total: $${total}K</strong>`;\n    },\n  },\n  grid: { left: 90, right: 60, top: 130, bottom: 70 },\n  xAxis: {\n    type: \"category\",\n    data: months,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Revenue ($K)\",\n    nameTextStyle: { color: t.inkSoft, fontSize: 14 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    ...components.map((c, i) => ({\n      name: c.name,\n      type: \"bar\",\n      stack: \"revenue\",\n      barWidth: \"55%\",\n      data: c.data,\n      // Rounded top corners on the topmost stack segment only, for polish.\n      ...(i === components.length - 1 && {\n        itemStyle: { borderRadius: [6, 6, 0, 0] },\n      }),\n      // Emphasize the composition-shift story: Subscriptions is the\n      // fastest-growing component (+150% Jan→Jun) — call it out on the\n      // final bar.\n      ...(i === 0 && {\n        label: {\n          show: true,\n          position: \"inside\",\n          color: \"#FFFFFF\",\n          fontSize: 12,\n          fontWeight: 600,\n          formatter: (params) =>\n            params.dataIndex === months.length - 1\n              ? `+${subsGrowthPct}%`\n              : \"\",\n        },\n      }),\n    })),\n    {\n      name: \"Total\",\n      type: \"bar\",\n      stack: \"revenue\",\n      data: months.map(() => 0),\n      itemStyle: { color: \"transparent\" },\n      label: {\n        show: true,\n        position: \"top\",\n        color: t.ink,\n        fontSize: 15,\n        fontWeight: 500,\n        formatter: (params) => totals[params.dataIndex],\n      },\n      tooltip: { show: false },\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        lineStyle: { color: t.inkSoft, type: \"dashed\", width: 1.5 },\n        label: {\n          color: t.inkSoft,\n          fontSize: 12,\n          formatter: `Avg total: $${avgTotal}K`,\n          position: \"insideEndTop\",\n        },\n        data: [{ yAxis: avgTotal }],\n      },\n    },\n  ],\n});\n"}