{"spec_id":"chessboard-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// chessboard-basic: Chess Board Grid Visualization\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-01\n//# anyplot-orientation: square\n// anyplot.ai\n// chessboard-basic: Chess Board Grid Visualization\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-09-01\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ScatterPlot } from \"@mui/x-charts/ScatterChart\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst FONT = \"system-ui, -apple-system, sans-serif\";\nconst TITLE = \"chessboard-basic · javascript · muix · anyplot.ai\";\n\n// Classic wood-board colors, drawn from the Imprint palette's semantic\n// exception (wood → ochre) plus the amber anchor for the lighter square —\n// both fixed across themes, like real board colors are.\nconst DARK_SQUARE = t.palette[3]; // \"#BD8233\" — ochre, wood\nconst LIGHT_SQUARE = t.amber; // \"#DDCC77\" — warm light wood\n\nconst COLUMNS = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"];\nconst RANKS_TOP_TO_BOTTOM = [\"8\", \"7\", \"6\", \"5\", \"4\", \"3\", \"2\", \"1\"];\n\n// 64 squares — a1 is dark, h1 is light (white's near-right corner), matching\n// standard chess-diagram convention. Square parity: (file index + rank - 1)\n// odd -> light square.\nconst squares = [];\nCOLUMNS.forEach((file, fileIdx) => {\n  for (let rank = 1; rank <= 8; rank += 1) {\n    const light = (fileIdx + rank - 1) % 2 === 1;\n    squares.push({ id: `${file}${rank}`, x: file, y: String(rank), light });\n  }\n});\n\n// Custom scatter mark: opaque squares tiling the band grid exactly (no gaps),\n// colored by the precomputed light/dark parity rather than a data value.\nfunction BoardSquares({ series, xScale, yScale }) {\n  const cellW = xScale.bandwidth();\n  const cellH = yScale.bandwidth();\n  return (\n    <g>\n      {series.data.map((pt) => (\n        <rect\n          key={pt.id}\n          x={xScale(pt.x) ?? 0}\n          y={yScale(pt.y) ?? 0}\n          width={cellW}\n          height={cellH}\n          fill={pt.light ? LIGHT_SQUARE : DARK_SQUARE}\n          stroke={t.pageBg}\n          strokeWidth={1.5}\n        />\n      ))}\n    </g>\n  );\n}\n\nexport default function Chart() {\n  const W = window.ANYPLOT_SIZE.width;\n  const H = window.ANYPLOT_SIZE.height;\n\n  // Reserve space for the title (top), rank labels (left) and file labels\n  // (bottom), then size the board to the largest square that fits the\n  // remaining area so every one of the 64 cells is a true square.\n  const TITLE_SPACE = 60;\n  const PAD_TOP = 20;\n  const PAD_RIGHT = 24;\n  const AXIS_LEFT = 60;\n  const AXIS_BOTTOM = 56;\n\n  const availW = W - AXIS_LEFT - PAD_RIGHT;\n  const availH = H - TITLE_SPACE - PAD_TOP - AXIS_BOTTOM;\n  const board = Math.min(availW, availH);\n\n  const margin = {\n    left: AXIS_LEFT + (availW - board) / 2,\n    right: PAD_RIGHT + (availW - board) / 2,\n    top: TITLE_SPACE + PAD_TOP + (availH - board) / 2,\n    bottom: AXIS_BOTTOM + (availH - board) / 2,\n  };\n\n  return (\n    <ChartContainer\n      width={W}\n      height={H}\n      margin={margin}\n      skipAnimation\n      series={[\n        {\n          type: \"scatter\",\n          id: \"board\",\n          data: squares,\n          color: DARK_SQUARE,\n        },\n      ]}\n      xAxis={[\n        {\n          scaleType: \"band\",\n          data: COLUMNS,\n          categoryGapRatio: 0,\n          disableTicks: true,\n          tickLabelStyle: { fontSize: 15, fill: t.inkSoft, fontFamily: FONT },\n        },\n      ]}\n      yAxis={[\n        {\n          scaleType: \"band\",\n          data: RANKS_TOP_TO_BOTTOM,\n          categoryGapRatio: 0,\n          disableTicks: true,\n          tickLabelStyle: { fontSize: 15, fill: t.inkSoft, fontFamily: FONT },\n        },\n      ]}\n    >\n      {/* Chart title */}\n      <text\n        x={W / 2}\n        y={36}\n        textAnchor=\"middle\"\n        fontSize={22}\n        fontWeight=\"500\"\n        fill={t.ink}\n        fontFamily={FONT}\n      >\n        {TITLE}\n      </text>\n\n      {/* Checkerboard squares, tiled edge-to-edge across the 8x8 band grid */}\n      <ScatterPlot slots={{ scatter: BoardSquares }} />\n\n      {/* Outer board frame */}\n      <rect\n        x={margin.left}\n        y={margin.top}\n        width={board}\n        height={board}\n        fill=\"none\"\n        stroke={t.ink}\n        strokeWidth={3}\n      />\n\n      {/* File labels (a-h) at the bottom, rank labels (1-8) on the left */}\n      <ChartsXAxis disableLine tickLabelStyle={{ fontSize: 15, fill: t.inkSoft, fontFamily: FONT }} />\n      <ChartsYAxis disableLine tickLabelStyle={{ fontSize: 15, fill: t.inkSoft, fontFamily: FONT }} />\n    </ChartContainer>\n  );\n}\n"}