{"spec_id":"bar-stacked-percent","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// bar-stacked-percent: 100% Stacked Bar Chart\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 95/100 | Created: 2026-08-18\n\nconst t = window.ANYPLOT_TOKENS;\n// \"muted\" anchor (other / rest) isn't in ANYPLOT_TOKENS — theme-adaptive per default-style-guide.md\nconst MUTED = window.ANYPLOT_THEME === \"light\" ? \"#6B6A63\" : \"#A8A79F\";\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Quarterly public-cloud infrastructure revenue by provider, $ billions.\n// Each bar normalizes its own row to 100% so quarter-over-quarter share\n// shifts are comparable even though the total market is growing.\nconst quarters = [\"Q1 2023\", \"Q2 2023\", \"Q3 2023\", \"Q4 2023\", \"Q1 2024\", \"Q2 2024\"];\nconst providers = [\"AWS\", \"Azure\", \"Google Cloud\", \"Alibaba Cloud\", \"Other\"];\nconst revenueByQuarter = [\n  [21.4, 14.3, 6.2, 3.9, 18.7],\n  [22.1, 15.8, 7.1, 3.8, 19.2],\n  [23.0, 17.2, 7.9, 3.6, 20.3],\n  [24.2, 18.9, 8.8, 3.5, 21.6],\n  [25.6, 20.8, 9.9, 3.3, 22.9],\n  [26.9, 22.5, 11.2, 3.1, 24.3],\n];\n\nconst shareByQuarter = revenueByQuarter.map((row) => {\n  const total = row.reduce((sum, value) => sum + value, 0);\n  return row.map((value) => (value / total) * 100);\n});\nconst totalByQuarter = revenueByQuarter.map((row) => row.reduce((sum, value) => sum + value, 0));\n\nconst datasets = providers.map((provider, i) => {\n  const isLeader = provider === \"AWS\";\n  return {\n    label: provider,\n    data: shareByQuarter.map((row) => row[i]),\n    backgroundColor: provider === \"Other\" ? MUTED : t.palette[i],\n    // Subtle page-bg seam between stacked segments (style guide \"Bar edges\"); the\n    // leader series gets an ink outline instead, calling out AWS's persistent lead.\n    borderColor: isLeader ? t.ink : t.pageBg,\n    borderWidth: isLeader ? 2 : 1,\n    borderSkipped: false,\n    categoryPercentage: 0.6,\n    barPercentage: 0.9,\n  };\n});\n\n// --- Segment percentage labels (custom Chart.js plugin) ---------------------\n// Draws a rounded percentage label centered in every segment tall enough to\n// hold it legibly, picking dark/light text per-segment from fill luminance\n// (YIQ) so labels stay readable across the whole palette in both themes.\nconst DARK_TEXT = \"#1A1A17\";\nconst LIGHT_TEXT = \"#F0EFE8\";\nconst MIN_LABEL_HEIGHT = 28; // px; smaller segments skip the label to avoid clutter/overflow\n\nfunction textColorFor(hex) {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  const yiq = (r * 299 + g * 587 + b * 114) / 1000;\n  return yiq >= 128 ? DARK_TEXT : LIGHT_TEXT;\n}\n\nconst segmentPercentLabels = {\n  id: \"segmentPercentLabels\",\n  afterDatasetsDraw(chart) {\n    const { ctx } = chart;\n    ctx.save();\n    ctx.font = \"600 13px sans-serif\";\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    chart.data.datasets.forEach((dataset, datasetIndex) => {\n      const meta = chart.getDatasetMeta(datasetIndex);\n      const labelColor = textColorFor(dataset.backgroundColor);\n      meta.data.forEach((bar, index) => {\n        const segmentHeight = Math.abs(bar.base - bar.y);\n        if (segmentHeight < MIN_LABEL_HEIGHT) return;\n        ctx.fillStyle = labelColor;\n        ctx.fillText(`${Math.round(dataset.data[index])}%`, bar.x, (bar.base + bar.y) / 2);\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: { labels: quarters, datasets },\n  plugins: [segmentPercentLabels],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"bar-stacked-percent · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22, weight: \"500\" },\n        padding: { bottom: 20 },\n      },\n      legend: {\n        position: \"bottom\",\n        labels: { color: t.ink, font: { size: 16 }, boxWidth: 20, padding: 18 },\n      },\n      tooltip: {\n        callbacks: {\n          title: (items) => `${items[0].label} — Total: $${totalByQuarter[items[0].dataIndex].toFixed(1)}B`,\n          label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y.toFixed(1)}%`,\n        },\n      },\n    },\n    scales: {\n      x: {\n        stacked: true,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n        title: { display: true, text: \"Quarter\", color: t.ink, font: { size: 16 } },\n      },\n      y: {\n        stacked: true,\n        min: 0,\n        max: 100,\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          stepSize: 20,\n          callback: (value) => `${value}%`,\n        },\n        grid: { color: t.grid },\n        title: {\n          display: true,\n          text: \"Share of Cloud Infrastructure Revenue\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n      },\n    },\n  },\n});\n"}