Workspace architecture
The chess workspace organizes around three sibling libraries plus their consumers (apps + the local dev harness):
graph TD M["Motif — design<br/>tokens · theme · components · assets"] T["Tabia — data<br/>Game tree · ReplayEngine<br/>PGN parser · UCI client · merge"] R["Rabbit — render<br/>pure SVG · variant-aware · themable"] APP["chess apps<br/>(and PROMOTE/dev/ — the local harness)"] M -->|visual| APP T -->|chess state| APP R -->|board pixels| APP classDef lib fill:#2563eb,color:#fff,stroke:#93c5fd,stroke-width:2px; class M,T,R lib;No project depends on another at the source level. Coordination
happens in the consuming app (or in PROMOTE/dev/, which is the
canonical worked example).
Layer responsibilities
Section titled “Layer responsibilities”Motif — visual identity, chess-aware UI components, assets
Section titled “Motif — visual identity, chess-aware UI components, assets”- Visual tokens (CSS custom properties): surface, text, shadow, radius, transition
- Chess-aware tokens: win/loss colors, NAG palette, state gradients
- Theme + board-style switcher (
design/theme.js) — small persistence- class-toggle module, no chess logic
- Chess-aware web components (
components/) — MoveList, EnginePanel, FenArea, PgnArea, BranchPopover, ContextMenu, EcoOpeningBar, PlayerHeader, SanPreview, Toolbar. Built on Lit. - Static reference data —
pieces/(39 piece SVG sets, licensing audited indocs/PIECE-LICENSING.md),data/eco-epd.json,data/test-pgns/ - Documented patterns (
docs/RECIPES.md,docs/KEYBOARD.md)
CSS is consumable on its own. Web components import lit; the host
app provides the lit module — typically by aliasing the bare
specifier to its own installed copy (see PROMOTE/dev/vite.config.js
for the harness pattern).
Tabia — chess data layer
Section titled “Tabia — chess data layer”- PGN parser + serializer (variant-agnostic)
createGame()— tree-shaped game state with variations, comments, NAGs, drawablesReplayEngine— step/seek primitive with position hashing- Collection layer (
games.js) — identity hashing, prefix detection, merge with attributed annotations - UCI engine client — generic, consumers provide the engine binary
Pure data. FEN / PGN / move-tree in and out. Pixels are someone else’s problem.
Rabbit — SVG board renderer
Section titled “Rabbit — SVG board renderer”- Pure SVG, framework-free
- Variant geometry support (standard 8×8 + non-rectangular layouts)
- Consumer-provided pieces (URL prefix, inline SVG, or loader fn)
- CSS-variable themable
- Driven imperatively via
setPosition,setShapes, etc.
Knows nothing about chess rules. Position in, pixels out.
What each project explicitly is not
Section titled “What each project explicitly is not”Motif is not
Section titled “Motif is not”- A framework. No router, no state-management opinion, no app shell.
- A logic layer. Anything that knows positions, moves, or PGN belongs in Tabia.
- A renderer. Boards are Rabbit’s job.
Tabia is not
Section titled “Tabia is not”- An app shell.
- A renderer. Tabia did briefly wrap chessground in
src/board.js; that was walked back. Tabia is data-only now — apps compose Tabia with a renderer at the app boundary. - A UI library. UI components live in Motif.
Rabbit is not
Section titled “Rabbit is not”- A game state engine. Rabbit renders whatever you tell it to render; it doesn’t validate moves or track variations.
- A piece-set provider. Consumers supply pieces via the
piecesoption. (Motif hosts the canonical piece-set library for those who want one.)
How a new app starts
Section titled “How a new app starts”mkdir my-new-chess-app && cd $_ && git init- Pull in Motif’s tokens —
<link rel="stylesheet" href=".../Motif/design/tokens.css"> - Apply the
motifclass to<html>. Importtheme.jsif you want runtime theme switching. - Import the slices of Tabia you need:
createGame,ReplayEngine,mergeGames, etc. - Mount Rabbit boards where you want pixels.
- Drop in Motif components (
<motif-move-list>,<motif-engine-panel>, etc.); feed them Tabia state via attributes/properties. - Write the app-shell glue that wires everything together.
The PROMOTE dev harness at ../../dev/ is the worked
example — it composes all three sibling libraries in one page.
What this is NOT
Section titled “What this is NOT”- Not a published npm package set (yet). Local imports via relative paths until there’s a stable external consumer.
- Not a framework. No router, no state-management opinion, no app shell. Bring your own.
- Not a permanent boundary. As consumers materialize and APIs settle, packages will firm up. For now the boundary is conceptual.