Customize & themes
Users can customize board appearance per meta-game. Route: /customize/:metaGame.
Key files
| File | Role |
|---|---|
Customize.js |
Theme customizer page |
ApplyCustomizationModal.js |
Bulk copy customization sections to other games |
mergeCustomizationSections.js |
Partial merge for bulk apply |
normalizeCustomizationSettings.js |
Legacy glyphmap / boardChrome → render.* on read |
resolveEffectiveRenderSettings.js |
Per-game vs _default render scope + validation |
getDisplayRenderRep.js |
Merge render.board / render.options into the game rep before draw |
customizeRenderSettings.js |
Customize UI ↔ render blob, preflight, size limit |
resolveEffectiveCustomCss.js |
Account vs localStorage CSS resolution |
useGameCustomCss.js |
Mount/unmount per-game CSS on game pages |
ThemeApplicator.js |
Applies stored site theme globally |
ThemeCustomizer.js |
Site chrome colour picker |
RenderOptionsModal.js |
Renderer display options (also on game page) |
setRendererColourOpts.js |
Passes colour context to renderer |
setGlyphMapOpt.js |
Glyph substitution map from resolved render.glyphmap |
Colour context
Zustand colourContext holds renderer colour overrides (background, strokes, labels, etc.). Light and dark presets are stored separately via react-use-storage-state in Skeleton:
stored-context-lightstored-context-dark
color-mode (light / dark) is also persisted and sets the color-mode attribute on <html>.
Customization blob
Saved via save_customization / delete_customization on the user profile (globalMe.customizations[metaGame]).
Top-level fields (unchanged)
colourContext— board drawing colourspalette— player colour slotspreferredColour— viewer seat colour when possiblecustomCss—{ css, active }account-backed per-game CSS
render — board layout, glyphs, rep options
New saves store renderer overrides under a single render object:
{
"render": {
"board": {
"style": "squares-checkered",
"strokeWeight": 2,
"labelScale": 1.25
},
"glyphmap": [
["piece", "meeple", 1.2]
],
"options": ["hide-star-points", "hide-labels"]
}
}
| Key | Applied via | Notes |
|---|---|---|
render.board |
applyBoardChrome → merged into APRenderRep before draw |
Partial boardBasic merge; no width / height |
render.glyphmap |
Renderer options.glyphmap |
[original, replacement, scale?] tuples |
render.options |
Replaces rep-level options when set |
e.g. hide-star-points, hide-labels |
board.labelScale scales row/column coordinate labels (replaces fragile custom CSS on SVG text for that purpose). See Renderer customization.
Board style swaps are only allowed within compatible groups (e.g. flat squares ↔ vertex on eligible games). Stacked boards cannot take a vertex-only style — preflight blocks save.
Markers: if render.board.markers is omitted, the game’s markers are kept (and sanitized in live play). If present (including []), markers are replaced.
Size limit: stringified render must be ≤ 8 KiB on Customize save.
Legacy fields (read-only compat)
Older profiles may still have:
- Top-level
glyphmap— treated asrender.glyphmapuntil the user saves again boardChrome— alias forrender.board
normalizeCustomizationSettings merges these on read. Customize and bulk apply write render only and drop legacy keys when saving.
Global defaults (_default) apply to games without a per-game customization entry.
Bulk apply
On the game-specific Customize page, Apply to other games… copies selected sections to other games. Requires saving first. Merges into each target’s existing blob (selected sections only).
Sections:
- Player colours (palette)
- Board colours (
colourContext) - Renderer (board, glyphs, options) — copies the whole
rendersubtree; sources with only legacyglyphmap/boardChromeare normalized intorenderon the target - Preferred colour
- Custom CSS
Custom CSS (dual source)
- Account —
customCssin the customization blob (edited on Customize, included in bulk apply). - Legacy localStorage —
custom-csskey, edited from the board CSS3 toolbar modal.
Precedence: account CSS (per-game or _default) wins; localStorage is used only when no account CSS resolves.
When opening Customize for a game with localStorage CSS but no account CSS, legacy CSS is imported into the editor (dirty until saved). Saving stores it in the account and removes the localStorage entry for that game.
On game pages, useGameCustomCss applies CSS via document.adoptedStyleSheets and clears it on unmount or game change.
Boards use div.board._meta_${metaGame} for CSS selectors.
SVG board DOM (custom CSS authors)
Player-facing customization how-to lives on the community wiki. This section is for developers and advanced users writing custom CSS on the Customize screen.
- Boards render as SVG inside
svg#theBoardSVG(some stacking games also usesvg#theStackSVG). - Common groups:
g#labels,g#gridlines,g#tiles,g#pieces(usuallyuseelements),defs/symbolfor glyphs (aprender-glyph-…ids). - Elements that follow player colours often have
data-playerfill=trueand/ordata-playerstroke=true. - Prefer Customize → coordinate label size over CSS for label scaling; prefer palette / board colours before overriding glyph fills in CSS.
- CSS variables on the board container (names may change):
--svg-label-color,--svg-gridline-color,--svg-default-fill,--svg-volcano-caps.
The board DOM is not a stable public API — verify selectors against a live game after renderer upgrades.
Thumbnails
Customize loads preset thumbnails from:
https://thumbnails.abstractplay.com/{metaGame}.json
Renderer integration
Customizations affect the JSON passed to @abstractplay/renderer render():
- Colours —
setRendererColourOpts - Display rep —
getDisplayRenderRep(rawRep, resolveEffectiveRenderSettings(...))thensanitizeRenderRepin live mode - Glyphs —
options.glyphmapfrom resolvedrender.glyphmap
See Renderer customization and Renderer docs for schema and registry rules.
Settings are saved to the user profile via auth queries and applied on game pages through useGameMoveSession.js and shared helpers in prepareBoardRender.js.