{"spec_id":"area-stacked","library":"muix","language":"javascript","code":"// anyplot.ai\n// area-stacked: Stacked Area Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-08-17\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\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\n// Tiny fixed-seed LCG — the browser has no seeded RNG\nfunction lcg(seed: number) {\n  let s = seed >>> 0;\n  return () => {\n    s = (Math.imul(1664525, s) + 1013904223) >>> 0;\n    return s / 4294967295;\n  };\n}\nconst rand = lcg(42);\n\n// Monthly website traffic sources, Jan 2024 – Dec 2025 (24 months)\nconst MONTHS = [\n  \"Jan\",\n  \"Feb\",\n  \"Mar\",\n  \"Apr\",\n  \"May\",\n  \"Jun\",\n  \"Jul\",\n  \"Aug\",\n  \"Sep\",\n  \"Oct\",\n  \"Nov\",\n  \"Dec\",\n];\nconst dates = Array.from({ length: 24 }, (_, i) => new Date(2024, i, 1));\n\n// Series ordered largest → smallest so the biggest source stacks at the base\nconst organicSearch = dates.map((_, i) =>\n  Math.round(\n    4200 +\n      i * 55 +\n      300 * Math.sin(((i - 6) * Math.PI) / 6) +\n      (rand() - 0.5) * 200,\n  ),\n);\nconst direct = dates.map((_, i) =>\n  Math.round(\n    2100 +\n      i * 22 +\n      120 * Math.sin(((i - 3) * Math.PI) / 6) +\n      (rand() - 0.5) * 140,\n  ),\n);\nconst referral = dates.map((_, i) =>\n  Math.round(\n    1150 + i * 9 + 80 * Math.sin(((i - 9) * Math.PI) / 6) + (rand() - 0.5) * 90,\n  ),\n);\nconst social = dates.map((_, i) =>\n  Math.round(\n    650 + i * 30 + 150 * Math.sin((i * Math.PI) / 6) + (rand() - 0.5) * 110,\n  ),\n);\n\n// Storytelling aids: the cumulative-total average (reference line) and the\n// fastest-growing channel's overall lift (callout), both derived from the data.\nconst totals = dates.map(\n  (_, i) => organicSearch[i] + direct[i] + referral[i] + social[i],\n);\nconst avgTotal = totals.reduce((sum, v) => sum + v, 0) / totals.length;\nconst socialGrowthPct = Math.round(\n  ((social[social.length - 1] - social[0]) / social[0]) * 100,\n);\n\nconst TITLE =\n  \"Website Traffic Sources · area-stacked · javascript · muix · anyplot.ai\";\nconst TITLE_H = 58;\n\n// --- Chart (default-exported component — the harness mounts it) ------------\n\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n\n  return (\n    <Box\n      sx={{\n        width,\n        height,\n        bgcolor: t.pageBg,\n        display: \"flex\",\n        flexDirection: \"column\",\n      }}\n    >\n      <Box\n        sx={{\n          height: TITLE_H,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"space-between\",\n          px: \"40px\",\n          pt: \"10px\",\n        }}\n      >\n        <Typography\n          sx={{\n            color: t.ink,\n            fontSize: \"22px\",\n            fontWeight: 600,\n            lineHeight: 1,\n          }}\n        >\n          {TITLE}\n        </Typography>\n        <Box\n          sx={{\n            display: \"flex\",\n            alignItems: \"center\",\n            gap: \"8px\",\n            bgcolor: t.elevatedBg,\n            borderRadius: \"6px\",\n            px: \"12px\",\n            py: \"6px\",\n          }}\n        >\n          <Box\n            sx={{\n              width: \"10px\",\n              height: \"10px\",\n              borderRadius: \"50%\",\n              bgcolor: t.palette[3],\n            }}\n          />\n          <Typography\n            sx={{\n              color: t.inkSoft,\n              fontSize: \"13px\",\n              fontWeight: 500,\n              lineHeight: 1,\n            }}\n          >\n            {`Social: fastest-growing channel, +${socialGrowthPct}% since Jan 2024`}\n          </Typography>\n        </Box>\n      </Box>\n\n      <LineChart\n        width={width}\n        height={height - TITLE_H}\n        skipAnimation\n        colors={t.palette.slice(0, 4)}\n        grid={{ horizontal: true }}\n        xAxis={[\n          {\n            data: dates,\n            scaleType: \"time\",\n            valueFormatter: (v: Date) =>\n              `${MONTHS[v.getMonth()]} ${v.getFullYear()}`,\n            tickNumber: 8,\n            label: \"Month\",\n          },\n        ]}\n        yAxis={[\n          {\n            min: 0,\n            label: \"Monthly Visits\",\n            valueFormatter: (v: number) => `${Math.round(v / 1000)}k`,\n          },\n        ]}\n        series={[\n          {\n            id: \"organic\",\n            data: organicSearch,\n            label: \"Organic Search\",\n            stack: \"traffic\",\n            area: true,\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n          {\n            id: \"direct\",\n            data: direct,\n            label: \"Direct\",\n            stack: \"traffic\",\n            area: true,\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n          {\n            id: \"referral\",\n            data: referral,\n            label: \"Referral\",\n            stack: \"traffic\",\n            area: true,\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n          {\n            id: \"social\",\n            data: social,\n            label: \"Social\",\n            stack: \"traffic\",\n            area: true,\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n        ]}\n        margin={{ top: 20, bottom: 90, left: 90, right: 40 }}\n        sx={{\n          \"& .MuiAreaElement-root\": { fillOpacity: 0.82 },\n          \"& .MuiLineElement-root\": { strokeWidth: 2 },\n          \"& .MuiLineElement-series-social\": { strokeWidth: 3 },\n          \"& .MuiChartsAxis-tickLabel\": { fontSize: \"14px\" },\n          \"& .MuiChartsAxis-label\": { fontSize: \"16px\" },\n          \"& .MuiChartsLegend-label\": { fontSize: \"14px\" },\n          \"& .MuiChartsGrid-line\": { stroke: t.grid, strokeWidth: 1 },\n        }}\n        slotProps={{\n          legend: {\n            position: { vertical: \"bottom\", horizontal: \"middle\" },\n            itemMarkWidth: 20,\n            itemMarkHeight: 4,\n          },\n        }}\n      >\n        <ChartsReferenceLine\n          y={avgTotal}\n          label={`Avg total: ${(avgTotal / 1000).toFixed(1)}k`}\n          labelAlign=\"start\"\n          lineStyle={{\n            stroke: t.inkSoft,\n            strokeDasharray: \"4 4\",\n            strokeWidth: 1.5,\n          }}\n          labelStyle={{\n            fill: t.inkSoft,\n            fontSize: 13,\n            fontWeight: 600,\n            stroke: t.pageBg,\n            strokeWidth: 5,\n            paintOrder: \"stroke\",\n          }}\n        />\n      </LineChart>\n    </Box>\n  );\n}\n"}