{"spec_id":"band-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// band-basic: Basic Band Plot\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-24\nimport { LineChart } from \"@mui/x-charts/LineChart\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: 30-day temperature forecast with widening uncertainty -----------\n// A short-range weather forecast: the further out the prediction, the wider\n// the 90% confidence band around the forecast temperature — the growing\n// uncertainty is the story, no annotations needed to make the point.\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1103515245 + 12345) % 2147483648;\n    return state / 2147483648;\n  };\n}\nconst random = lcg(42);\n\nconst forecastDays = Array.from({ length: 30 }, (_, i) => i + 1);\n\nconst forecastTemp = forecastDays.map((day) => {\n  const seasonalDrift = 16 + 5 * Math.sin((day / 30) * Math.PI * 0.9);\n  const warmingTrend = day * 0.1;\n  const noise = (random() - 0.5) * 0.9;\n  return Math.round((seasonalDrift + warmingTrend + noise) * 10) / 10;\n});\n\nconst lowerBound = forecastTemp.map((value, i) => {\n  const halfWidth = 0.6 + forecastDays[i] * 0.13 + random() * 0.3;\n  return Math.round((value - halfWidth) * 10) / 10;\n});\n\nconst upperBound = forecastTemp.map((value, i) => {\n  const halfWidth = 0.6 + forecastDays[i] * 0.13 + random() * 0.3;\n  return Math.round((value + halfWidth) * 10) / 10;\n});\n\n// Stacked-area trick: an invisible \"lower\" series carries the offset, and\n// \"band\" (the visible fill) is stacked on top of it with only its own width\n// (upper - lower) — the rendered area then spans exactly [lower, upper].\nconst bandWidth = upperBound.map((value, i) => Math.round((value - lowerBound[i]) * 10) / 10);\n\n// --- Title + legend chrome ---------------------------------------------------\n// The built-in <ChartsLegend> anchors to the SVG's own total height rather\n// than reserving extra space for itself, so it gets clipped at the canvas\n// edge instead of pushing the plot up — a hand-rolled legend row in normal\n// flex flow (like arc-basic/muix.tsx) guarantees it fits.\nconst TITLE = \"Forecast Uncertainty · band-basic · javascript · muix · anyplot.ai\";\nconst TITLE_FONT_DEFAULT = 22;\nconst titleFontSize =\n  TITLE.length > 67 ? Math.round(TITLE_FONT_DEFAULT * (67 / TITLE.length)) : TITLE_FONT_DEFAULT;\nconst TITLE_H = 42;\nconst LEGEND_H = 34;\n\nfunction Legend() {\n  return (\n    <div style={{ height: LEGEND_H, display: \"flex\", alignItems: \"center\", gap: \"22px\" }}>\n      <div style={{ display: \"flex\", alignItems: \"center\", gap: \"7px\" }}>\n        <span\n          style={{\n            width: \"18px\",\n            height: \"14px\",\n            backgroundColor: t.palette[0],\n            opacity: 0.28,\n            display: \"inline-block\",\n          }}\n        />\n        <span style={{ fontSize: \"14px\", color: t.inkSoft }}>90% confidence band</span>\n      </div>\n      <div style={{ display: \"flex\", alignItems: \"center\", gap: \"7px\" }}>\n        <span style={{ width: \"18px\", height: \"3px\", backgroundColor: t.palette[2], display: \"inline-block\" }} />\n        <span style={{ fontSize: \"14px\", color: t.inkSoft }}>Forecast temperature</span>\n      </div>\n    </div>\n  );\n}\n\n// --- Chart (default-exported component — the harness mounts it) ------------\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const chartHeight = height - TITLE_H - LEGEND_H;\n\n  return (\n    <div style={{ width, height, display: \"flex\", flexDirection: \"column\" }}>\n      <div style={{ paddingLeft: \"80px\" }}>\n        <div\n          style={{\n            height: `${TITLE_H}px`,\n            lineHeight: `${TITLE_H}px`,\n            fontSize: `${titleFontSize}px`,\n            fontWeight: 500,\n            color: t.ink,\n          }}\n        >\n          {TITLE}\n        </div>\n        <Legend />\n      </div>\n      <LineChart\n        width={width}\n        height={chartHeight}\n        skipAnimation\n        grid={{ horizontal: true }}\n        xAxis={[\n          {\n            data: forecastDays,\n            scaleType: \"linear\",\n            label: \"Forecast Day\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n          },\n        ]}\n        yAxis={[\n          {\n            label: \"Temperature (°C)\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n          },\n        ]}\n        series={[\n          {\n            id: \"lower\",\n            data: lowerBound,\n            stack: \"band\",\n            showMark: false,\n            color: t.pageBg,\n            curve: \"natural\",\n          },\n          {\n            id: \"band\",\n            data: bandWidth,\n            stack: \"band\",\n            area: true,\n            showMark: false,\n            color: t.palette[0],\n            curve: \"natural\",\n          },\n          {\n            id: \"center\",\n            data: forecastTemp,\n            showMark: false,\n            color: t.palette[2],\n            curve: \"natural\",\n          },\n        ]}\n        margin={{ top: 20, right: 40, bottom: 70, left: 80 }}\n        sx={{\n          \"& .MuiLineElement-series-lower\": { display: \"none\" },\n          \"& .MuiAreaElement-series-band\": { fill: t.palette[0], fillOpacity: 0.28 },\n          \"& .MuiLineElement-series-band\": { stroke: \"none\" },\n          \"& .MuiLineElement-series-center\": { strokeWidth: 3 },\n        }}\n      />\n    </div>\n  );\n}\n"}