{"spec_id":"bar-stacked-percent","library":"muix","language":"javascript","code":"// anyplot.ai\n// bar-stacked-percent: 100% Stacked Bar Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 94/100 | Created: 2026-08-18\nimport { BarChart } from \"@mui/x-charts/BarChart\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Fixed (non-theme-flipping) text colors for in-bar percentage labels — the\n// segment fills stay the same hex in both themes, so label contrast is chosen\n// per series color rather than following t.ink/t.inkSoft.\nconst LABEL_ON_LIGHT_FILL = \"#1A1A17\"; // dark ink — for the green/lavender/ochre segments\nconst LABEL_ON_DARK_FILL = \"#F0EFE8\"; // light ink — for the darker blue segment\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Electricity generation mix (TWh/year, illustrative) for six grids whose\n// total output spans two orders of magnitude — China dwarfs Norway in raw\n// terawatt-hours, which is exactly why a percent-stacked view (composition)\n// tells a clearer story here than a raw stacked view (dominated by scale).\nconst rawByCountry = {\n  China: { renewables: 3100, nuclear: 400, gas: 300, coal: 5200 },\n  USA: { renewables: 900, nuclear: 800, gas: 1700, coal: 700 },\n  Germany: { renewables: 270, nuclear: 0, gas: 80, coal: 150 },\n  France: { renewables: 120, nuclear: 320, gas: 30, coal: 5 },\n  Brazil: { renewables: 550, nuclear: 15, gas: 60, coal: 15 },\n  Norway: { renewables: 150, nuclear: 0, gas: 0.5, coal: 0.1 },\n};\n\n// Sorted descending by renewables share so the bars themselves form a visual\n// gradient (all-renewable Norway -> coal-heavy China) that reinforces the\n// subtitle's contrast, rather than an arbitrary country ordering.\nconst renewablesShare = (country) => {\n  const raw = rawByCountry[country];\n  const total = raw.renewables + raw.nuclear + raw.gas + raw.coal;\n  return raw.renewables / total;\n};\nconst countries = Object.keys(rawByCountry).sort((a, b) => renewablesShare(b) - renewablesShare(a));\n\n// Renewables listed first so it lands on Imprint position 1 (brand green) —\n// a natural semantic fit (growth/nature). Order stays fixed across every bar.\nconst components = [\n  { id: \"renewables\", label: \"Renewables\", labelFill: LABEL_ON_LIGHT_FILL },\n  { id: \"nuclear\", label: \"Nuclear\", labelFill: LABEL_ON_LIGHT_FILL },\n  { id: \"gas\", label: \"Natural Gas\", labelFill: LABEL_ON_DARK_FILL },\n  { id: \"coal\", label: \"Coal\", labelFill: LABEL_ON_LIGHT_FILL },\n];\nconst labelFillById = Object.fromEntries(components.map((c) => [c.id, c.labelFill]));\n\nconst totalsByCountry = countries.map((country) =>\n  components.reduce((sum, c) => sum + rawByCountry[country][c.id], 0),\n);\nconst series = components.map((c) => ({\n  id: c.id,\n  label: c.label,\n  stack: \"total\",\n  data: countries.map((country, i) => (rawByCountry[country][c.id] / totalsByCountry[i]) * 100),\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: 22, fontWeight: 500 }}>\n          bar-stacked-percent · javascript · muix · anyplot.ai\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 14, mt: 0.5 }}>\n          Norway's grid runs almost entirely on renewables, while China still leans on coal\n        </Typography>\n      </Box>\n\n      {/* Bar chart */}\n      <Box\n        sx={{\n          position: \"absolute\",\n          top: CHART_TOP,\n          left: 0,\n          right: 0,\n          bottom: 0,\n        }}\n      >\n        <BarChart\n          width={W}\n          height={H - CHART_TOP}\n          colors={t.palette}\n          skipAnimation\n          xAxis={[\n            {\n              scaleType: \"band\",\n              data: countries,\n              label: \"Country\",\n              disableTicks: true,\n              labelStyle: { fontSize: 15, fill: t.ink },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n              categoryGapRatio: 0.35,\n            },\n          ]}\n          yAxis={[\n            {\n              label: \"Share (%)\",\n              labelStyle: { fontSize: 15, fill: t.ink },\n              // tickFontSize (unlike tickLabelStyle.fontSize) drives the axis\n              // label's offset from the tick labels — pushed wide to clear\n              // the \"100%\" tick text without inflating its visual size.\n              tickFontSize: 70,\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n              disableTicks: true,\n              min: 0,\n              max: 100,\n              valueFormatter: (v) => `${v}%`,\n            },\n          ]}\n          series={series.map((s) => ({\n            ...s,\n            valueFormatter: (v) => `${v.toFixed(1)}%`,\n          }))}\n          barLabel={(item) => (item.value != null && item.value >= 6 ? `${item.value.toFixed(0)}%` : null)}\n          margin={{ top: 14, right: 90, bottom: 90, left: 140 }}\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            barLabel: (ownerState) => ({\n              style: {\n                fontSize: 13,\n                fontWeight: 600,\n                fill: labelFillById[ownerState.seriesId] ?? LABEL_ON_LIGHT_FILL,\n              },\n            }),\n          }}\n          sx={{\n            // Lighter, grid-weight axis line instead of the default full-ink\n            // stroke — a more refined chrome treatment than the library default.\n            \"& .MuiChartsAxis-line\": { stroke: t.grid },\n            \"& .MuiChartsGrid-line\": { stroke: t.grid },\n          }}\n        />\n      </Box>\n    </Box>\n  );\n}\n"}