{"spec_id":"bar-grouped","library":"muix","language":"javascript","code":"// anyplot.ai\n// bar-grouped: Grouped Bar Chart\n// Library: muix 7.29.1 | JavaScript 22.23.1\n// Quality: 92/100 | Created: 2026-08-05\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 value labels — the bar\n// fills stay the same hex in both themes, so the 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 bars\nconst LABEL_ON_DARK_FILL = \"#F0EFE8\"; // light ink — for the darker blue bars\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Quarterly revenue (in $M) for three product lines. Hardware peaks in Q2\n// then plateaus/declines as the mix shifts toward recurring revenue: Software\n// overtakes Hardware in Q3, and Services closes the gap to overtake it too by\n// Q4 — a genuine rank crossover rather than three lines rising in lockstep.\nconst quarters = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"];\nconst productLines = [\n  { id: \"hardware\", label: \"Hardware\", data: [4.2, 4.8, 4.5, 4.2], labelFill: LABEL_ON_LIGHT_FILL },\n  { id: \"software\", label: \"Software\", data: [3.1, 3.9, 4.7, 5.6], labelFill: LABEL_ON_LIGHT_FILL },\n  { id: \"services\", label: \"Services\", data: [2.0, 2.6, 3.2, 4.4], labelFill: LABEL_ON_DARK_FILL },\n];\nconst labelFillById = Object.fromEntries(productLines.map((p) => [p.id, p.labelFill]));\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-grouped · javascript · muix · anyplot.ai\n        </Typography>\n        <Typography sx={{ color: t.inkSoft, fontSize: 14, mt: 0.5 }}>\n          Software overtakes Hardware in Q3 as Services closes the gap by Q4\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          borderRadius={4}\n          xAxis={[\n            {\n              scaleType: \"band\",\n              data: quarters,\n              label: \"Fiscal Quarter\",\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: \"Revenue ($M)\",\n              labelStyle: { fontSize: 15, fill: t.ink },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n              disableTicks: true,\n              max: 6.4,\n            },\n          ]}\n          series={productLines.map((p) => ({\n            id: p.id,\n            label: p.label,\n            data: p.data,\n            valueFormatter: (v) => `$${v.toFixed(1)}M`,\n          }))}\n          barLabel={(item) => (item.value != null ? `$${item.value.toFixed(1)}M` : null)}\n          margin={{ top: 14, right: 28, bottom: 90, 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            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            \"& .MuiChartsAxis-line\": { stroke: t.inkSoft },\n            \"& .MuiChartsGrid-line\": { stroke: t.grid },\n          }}\n        />\n      </Box>\n    </Box>\n  );\n}\n"}