Spaces:
Running
Running
Download frontend/src/engine/theme.js from chesstb/explorer: direct link, hf CLI and curl.
- Browser
- Download file 6.73 kB
-
https://huggingface.co/spaces/chesstb/explorer/resolve/main/frontend/src/engine/theme.js
- Command line
-
hf download hf://spaces/chesstb/explorer/frontend/src/engine/theme.js
-
curl -L -o theme.js https://huggingface.co/spaces/chesstb/explorer/resolve/main/frontend/src/engine/theme.js
6.73 kB
| /** | |
| * theme.js: Board and piece theming module | |
| * | |
| * Owns the chess board's visual styling: square colours, rank/file label | |
| * colours, last-move highlight colour, and the piece-set image path. Colours | |
| * are read from the --board-* tokens in styles/tokens.css. The board uses | |
| * Libre Brown and the piece set uses CBurnett. | |
| * Exposes a small public API consumed by board.js and useExplorer.js. | |
| * | |
| * The CSS-generation function below targets chessground's DOM/class model | |
| * (`cg-board`, `coords.ranks`/`coords.files`) - see the comment above | |
| * _coordLabelCss() for how coordinate-label positioning and colouring are | |
| * derived against that model. | |
| * | |
| * Public API: | |
| * Theme.init(reconstructBoard) - register board reconstruction callback | |
| * Theme.apply() - inject the board's theme <style> tag | |
| * Theme.currentPieceSet() - return the active piece set id string | |
| * Theme.pieceThemeFn() - return piece-code → image-path function | |
| * (used by board.js's spare trays and | |
| * promotion dialog) | |
| */ | |
| function _token(name) { | |
| return getComputedStyle(document.documentElement).getPropertyValue(name).trim(); | |
| } | |
| function _boardColors() { | |
| const light = _token('--board-light'); | |
| const dark = _token('--board-dark'); | |
| return { | |
| id: 'libre-brown', | |
| lightSquare: light, | |
| darkSquare: dark, | |
| labelLight: dark, // label colour on light-square corners | |
| labelDark: light, // label colour on dark-square corners | |
| highlight: _token('--board-highlight'), | |
| }; | |
| } | |
| const PIECE_SET = 'cburnett'; | |
| let _reconstructBoard = null; // callback registered via init() | |
| // ── Private: CSS generation ─────────────────────────────────────────────── | |
| // | |
| // Chessground has no per-square light/dark CSS classes - the checkerboard | |
| // pattern is a single background-image on `cg-board`. It is rendered here | |
| // as a tiny tiled SVG data URI (_checkerboardDataUri). Rank/file labels | |
| // are chessground's own `coords.ranks`/`coords.files` elements, positioned | |
| // by static CSS in chess/board.css to sit inset at the board's corners | |
| // rather than chessground's default outside-the-board placement. Colour is | |
| // done with :nth-child selectors scoped by orientation, not the | |
| // `.coord-light`/`.coord-dark` classes renderCoords() (wrap.ts) puts on | |
| // each label: those classes don't correspond to the actual square each | |
| // label overlays once repositioned this way (chessground's own official | |
| // theme CSS doesn't use them for coords.ranks/coords.files either, for the | |
| // same reason: they're only meaningful for chessground's default | |
| // outside-the-board placement). See the matching comment in | |
| // chess/board.css for the full derivation. | |
| function _checkerboardDataUri(light, dark) { | |
| const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='2' height='2'> | |
| <rect width='1' height='1' fill='${light}'/> | |
| <rect x='1' width='1' height='1' fill='${dark}'/> | |
| <rect y='1' width='1' height='1' fill='${dark}'/> | |
| <rect x='1' y='1' width='1' height='1' fill='${light}'/> | |
| </svg>`; | |
| return `data:image/svg+xml;base64,${btoa(svg)}`; | |
| } | |
| // A light-square label gets c.labelLight, a dark-square label gets | |
| // c.labelDark. class + type + type + :nth-child, matched on all four | |
| // orientation/parity combinations, carries the specificity needed to | |
| // override chess/board.css's own default coord-label rule (see the comment there | |
| // for why .coord-light/.coord-dark aren't used) regardless of source | |
| // order. For white orientation the odd nth-child positions land on the | |
| // a-file's dark squares (a1/a3/a5/a7, per chess/board.css's comment) and so take | |
| // c.labelDark; the even positions (a2/a4/a6/a8, light squares) take | |
| // c.labelLight. No text-shadow: the label colour itself carries enough | |
| // contrast against its own square. | |
| function _coordLabelCss(c) { | |
| return `.orientation-white coords.ranks coord:nth-child(odd), | |
| .orientation-white coords.files coord:nth-child(odd) { color: ${c.labelDark} !important; } | |
| .orientation-white coords.ranks coord:nth-child(even), | |
| .orientation-white coords.files coord:nth-child(even) { color: ${c.labelLight} !important; } | |
| .orientation-black coords.ranks coord:nth-child(odd), | |
| .orientation-black coords.files coord:nth-child(odd) { color: ${c.labelLight} !important; } | |
| .orientation-black coords.ranks coord:nth-child(even), | |
| .orientation-black coords.files coord:nth-child(even) { color: ${c.labelDark} !important; }`; | |
| } | |
| function _cssForBoard(c) { | |
| return `/* chesstb-theme: ${c.id} */ | |
| cg-board { | |
| background-image: url("${_checkerboardDataUri(c.lightSquare, c.darkSquare)}"); | |
| /* _checkerboardDataUri() returns a 2x2 tile (2 squares per axis). | |
| 25% 25% sizes it to 2/8 of the board per axis, so the tile repeats | |
| 4x4 (background-repeat's default) to fill all 64 squares; matches | |
| the base checkerboard sizing in chess/board.css. */ | |
| background-size: 25% 25%; | |
| } | |
| ${_coordLabelCss(c)} | |
| cg-board square.last-move { background-color: ${c.highlight} !important; }`; | |
| } | |
| function _applyCSS() { | |
| let tag = document.getElementById('chesstb-theme'); | |
| if (!tag) { | |
| tag = document.createElement('style'); | |
| tag.id = 'chesstb-theme'; | |
| document.head.appendChild(tag); | |
| } | |
| tag.textContent = `@layer app {\n${_cssForBoard(_boardColors())}\n}`; | |
| } | |
| // ── Public API ──────────────────────────────────────────────────────────── | |
| /** | |
| * Register the board reconstruction callback from board.js. | |
| * Must be called before Board.init(). | |
| */ | |
| function init(reconstructBoard) { | |
| _reconstructBoard = reconstructBoard; | |
| } | |
| /** | |
| * Inject the board's theme <style> tag and populate the piece-set-dependent | |
| * tray/promotion images. | |
| */ | |
| function apply() { | |
| _applyCSS(); | |
| if (_reconstructBoard) _reconstructBoard(); | |
| } | |
| /** Return the active piece set id string. */ | |
| function currentPieceSet() { return PIECE_SET; } | |
| // Built once - pieceThemeFn() below always returns this same function, | |
| // rather than allocating a fresh closure per call. | |
| function _pieceThemePath(piece) { | |
| return `/static/pieces/${PIECE_SET}/${piece}.svg`; | |
| } | |
| /** | |
| * Return the piece-code → image-path function for the active piece set | |
| * (e.g. 'wQ' -> '/static/pieces/cburnett/wQ.svg'). Called by board.js to | |
| * populate the spare-tray and promotion-dialog images. | |
| */ | |
| function pieceThemeFn() { | |
| return _pieceThemePath; | |
| } | |
| export const Theme = { | |
| init, apply, | |
| currentPieceSet, | |
| pieceThemeFn, | |
| }; | |