{"spec_id":"bar-stacked","library":"muix","language":"javascript","code":"// anyplot.ai\n// bar-stacked: Stacked Bar Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 87/100 | Created: 2026-09-02\nimport { BarChart } from \"@mui/x-charts/BarChart\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Monthly cloud infrastructure spend ($K), broken down by service. Compute\n// grows fastest of the four, so its share of the stack widens month over\n// month — a composition story a stacked view tells better than a line chart.\nconst months = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"];\nconst spendByMonth = {\n  Jan: { compute: 38, storage: 14, networking: 9, database: 21 },\n  Feb: { compute: 41, storage: 15, networking: 10, database: 22 },\n  Mar: { compute: 44, storage: 16, networking: 10, database: 24 },\n  Apr: { compute: 47, storage: 17, networking: 11, database: 25 },\n  May: { compute: 52, storage: 18, networking: 12, database: 27 },\n  Jun: { compute: 58, storage: 20, networking: 13, database: 29 },\n};\n\n// Stack order fixed across every bar, largest/most-volatile service first so\n// it lands on Imprint position 1 (brand green).\nconst components = [\n  { id: \"compute\", label: \"Compute\" },\n  { id: \"storage\", label: \"Storage\" },\n  { id: \"networking\", label: \"Networking\" },\n  { id: \"database\", label: \"Database\" },\n];\n\nconst series = components.map((c) => ({\n  id: c.id,\n  label: c.label,\n  stack: \"total\",\n  data: months.map((m) => spendByMonth[m][c.id]),\n  valueFormatter: (v) => `$${v}K`,\n}));\n\n// Stack totals, used for the total-value labels above each bar and to give\n// the y-axis enough headroom that those labels never clip against the top.\nconst totals = months.map((m) => {\n  const s = spendByMonth[m];\n  return s.compute + s.storage + s.networking + s.database;\n});\nconst yMax = Math.ceil((Math.max(...totals) * 1.15) / 10) * 10;\n\n// Total labels above each stack (per the spec's \"Notes\" suggestion), placed\n// via the chart's own scales so they track the bars exactly.\nfunction TotalLabels() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  return (\n    <g>\n      {months.map((m, i) => (\n        <text\n          key={m}\n          x={xScale(m) + xScale.bandwidth() / 2}\n          y={yScale(totals[i]) - 10}\n          textAnchor=\"middle\"\n          fontSize={14}\n          fontWeight={600}\n          fill={t.ink}\n        >\n          {`$${totals[i]}K`}\n        </text>\n      ))}\n    </g>\n  );\n}\n\nexport default function Chart() {\n  const W = window.ANYPLOT_SIZE.width; // 1600 CSS px (landscape mount)\n  const H = window.ANYPLOT_SIZE.height; // 900 CSS px\n  const CHART_TOP = 84;\n\n  return (\n    <Box sx={{ position: \"relative\", width: W, height: H, bgcolor: t.pageBg }}>\n      {/* Title + subtitle */}\n      <Box sx={{ position: \"absolute\", top: 24, left: 56, right: 56 }}>\n        <Typography sx={{ color: t.ink, fontSize: 21, fontWeight: 500 }}>\n          Cloud Costs by Service · bar-stacked · javascript · muix · anyplot.ai\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 14, mt: 0.5 }}>\n          Compute spend nearly doubles by June, driving most of the total stack's growth\n        </Typography>\n      </Box>\n\n      {/* Bar chart */}\n      <Box sx={{ position: \"absolute\", top: CHART_TOP, left: 0, right: 0, bottom: 0 }}>\n        <BarChart\n          width={W}\n          height={H - CHART_TOP}\n          colors={t.palette}\n          skipAnimation\n          borderRadius={3}\n          axisHighlight={{ x: \"none\", y: \"none\" }}\n          tooltip={{ trigger: \"none\" }}\n          xAxis={[\n            {\n              scaleType: \"band\",\n              data: months,\n              label: \"Month\",\n              disableTicks: true,\n              labelStyle: { fontSize: 15, fill: t.ink },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n              categoryGapRatio: 0.4,\n            },\n          ]}\n          yAxis={[\n            {\n              label: \"Spend ($K)\",\n              labelStyle: { fontSize: 15, fill: t.ink },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n              disableTicks: true,\n              max: yMax,\n            },\n          ]}\n          series={series}\n          margin={{ top: 14, right: 170, bottom: 70, left: 90 }}\n          grid={{ horizontal: true }}\n          slotProps={{\n            legend: {\n              position: { vertical: \"middle\", horizontal: \"right\" },\n              direction: \"column\",\n              labelStyle: { fontSize: 14, fill: t.inkSoft },\n            },\n          }}\n          sx={{\n            \"& .MuiChartsAxis-line\": { stroke: t.grid },\n            \"& .MuiChartsGrid-line\": { stroke: t.grid },\n            \"& .MuiBarElement-root\": { stroke: t.pageBg, strokeWidth: 1 },\n          }}\n        >\n          <TotalLabels />\n        </BarChart>\n      </Box>\n    </Box>\n  );\n}\n"}