๐ Neo Chess Board
โจ Features
๐ฏ Modern & Lightweight
- ๐ฆ Zero dependencies (React is peer dependency)
- ๐ชถ Minimal bundle size
- โก High performance Canvas rendering
- ๐ง Full TypeScript support
๐ฎ Rich Chess Experience
- ๐ฑ๏ธ Smooth drag & drop interactions
- ๐จ Beautiful piece sprites with shadows
- โจ Fluid animations and transitions
- ๐ฏ Legal move highlighting
- ๐ Optional auto-flip to follow the side to move
- ๐ฑ Responsive design
๐ง Developer Friendly
- ๐ ฐ๏ธ Complete TypeScript types
- โ๏ธ React hooks ready
- ๐ Advanced PGN Management (import/export with annotations)
- ๐จ Customizable themes
- ๐งช Jest setup for automated testing
๐ช Advanced Features
- ๐ Built-in PGN recorder
- ๐ญ Multiple visual themes
- ๐ FEN support
- ๐ฎ Custom rules engine
- ๐น Visual PGN Annotations (arrows & circles)
- ๐ Square names stay aligned to the bottom and left edges in every orientation
๐ Quick Start
Installation
Configure npm to use the GitHub Packages registry for the @magicolala
scope before installing. Generate a GitHub Personal Access Token with the read:packages
permission and add it to your .npmrc
:
# .npmrc
@magicolala:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
Replace ${GITHUB_TOKEN}
with your token or an environment variable. Then install the package:
npm install @magicolala/neo-chess-board
# or
yarn add @magicolala/neo-chess-board
# or
pnpm add @magicolala/neo-chess-board
React Usage
import React, { useState } from 'react';
import { NeoChessBoard } from '@magicolala/neo-chess-board/react';
function ChessApp() {
const [fen, setFen] = useState();
return (
<NeoChessBoard
theme="midnight"
onMove={({ from, to, fen }) => {
console.log(`Move: ${from} โ ${to}`);
setFen(fen);
}}
style={{ width: '400px', height: '400px' }}
/>
);
}
Vanilla JavaScript
import { NeoChessBoard } from '@magicolala/neo-chess-board';
const board = new NeoChessBoard(document.getElementById('board'), {
theme: 'classic',
interactive: true,
showCoordinates: true,
});
board.on('move', ({ from, to, fen }) => {
console.log(`Move: ${from} โ ${to}`);
});
๐จ Themes
Neo Chess Board comes with beautiful built-in themes:
Theme | Preview | Colors |
---|---|---|
Classic | Light & clean design | |
Midnight | Dark & modern feel |
Use registerTheme('sunset', customTheme)
to add reusable presets. Custom theme objects can also be passed directly to constructors, setTheme
, or the React component.
๐ Theme Creator Web App
Designing colors manually can be slow. Visit the interactive Theme Creator to preview palettes live and export the resulting code. The builder includes:
- ๐๏ธ Real-time controls for every theme property (board colors, highlights, arrows, etc.)
- ๐พ Quick access to existing presets so you can start from
classic
ormidnight
- ๐ค Export helpers that copy JSON or TypeScript snippets using
registerTheme
Try it out
- Open the hosted Theme Creator. For local work run
npm run dev
insidedemo/
and navigate tohttp://localhost:5174/theme-creator.html
. - Choose a base theme, tweak the color pickers, and watch the preview board update instantly.
- Name the palette, save it, and download the generated JSON or TypeScript code.
You can then register the exported object in your project:
import { registerTheme } from '@magicolala/neo-chess-board';
const aurora = {
light: '#F5F3FF',
dark: '#1E1B4B',
// ...rest of the properties from the generator
};
registerTheme('aurora', aurora);
The Theme Creator keeps saved palettes in localStorage
, so you can revisit and refine them anytime.
๐ Documentation
Core Components
NeoChessBoard (React)
interface NeoChessProps {
fen?: string; // Chess position in FEN notation
position?: string; // Alias for FEN when integrating with other APIs
theme?: ThemeName | Theme; // Built-in theme name or custom object
orientation?: 'white' | 'black'; // Board orientation
boardOrientation?: 'white' | 'black'; // Orientation alias
chessboardRows?: number; // Number of ranks to render
chessboardColumns?: number; // Number of files to render
interactive?: boolean; // Enable drag & drop
showCoordinates?: boolean; // Show file/rank labels
animationMs?: number; // Animation duration (legacy alias)
animationDurationInMs?: number; // Preferred animation duration alias
showAnimations?: boolean; // Toggle move animations
highlightLegal?: boolean; // Highlight legal moves
allowDragging?: boolean; // Enable pointer dragging
dragActivationDistance?: number; // Pixels required before a drag starts
allowDragOffBoard?: boolean; // Allow dropping outside the board to cancel
allowAutoScroll?: boolean; // Auto-scroll parent containers near edges
allowDrawingArrows?: boolean; // Enable right-click arrow drawing
clearArrowsOnClick?: boolean; // Clear arrows with a left click
arrowOptions?: { color?: string; width?: number; opacity?: number }; // Default arrow styling
arrows?: Arrow[]; // Controlled arrow collection
onArrowsChange?: (arrows: Arrow[]) => void; // Called when arrows change
canDragPiece?: (params: { square: Square; piece: string; board: NeoChessBoard }) => boolean; // Filter draggable pieces
onMove?: (move) => void; // Move event handler
onIllegal?: (attempt) => void; // Illegal move handler
style?: React.CSSProperties; // CSS styling
className?: string; // CSS class
}
Core Chess Engine
// Initialize board
const board = new NeoChessBoard(element, options);
// Event handling
board.on('move', ({ from, to, fen }) => {
// Handle move
});
board.on('illegal', ({ from, to, reason }) => {
// Handle illegal move attempt
});
// Position management
board.setPosition('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
const currentFEN = board.getPosition();
PGN Recording & Annotations
import { PgnNotation } from '@magicolala/neo-chess-board';
const pgn = new PgnNotation();
// Set game metadata
pgn.setMetadata({
Event: 'Annotated Game',
White: 'Player A',
Black: 'Player B',
Date: '2024.09.15'
});
// Add moves with comments and visual annotations
pgn.addMove(1, 'e4', 'e5', 'White starts with king's pawn.', '{%cal Ge2e4,Re7e5}');
pgn.addMove(2, 'Nf3', 'Nc6', 'Knights develop.', '{%csl Gf3,Gc6}');
// Generate PGN with annotations
const pgnText = pgn.toPgnWithAnnotations();
console.log(pgnText);
/*
[Event "Annotated Game"]
[Site "Neo Chess Board"]
[Date "2024.09.15"]
[Round "1"]
[White "Player A"]
[Black "Player B"]
[Result "*"]
1. e4 {%cal Ge2e4,Re7e5} e5 {White starts with king's pawn.}
2. Nf3 {%csl Gf3,Gc6} Nc6 {Knights develop.}
*/
// Download PGN
pgn.downloadPgn('annotated_game.pgn');
๐ช Advanced Examples
Complete Chess Application
import React, { useState, useMemo } from 'react';
import { PGNRecorder } from '@magicolala/neo-chess-board';
import { NeoChessBoard } from '@magicolala/neo-chess-board/react';
function ChessGame() {
const [fen, setFen] = useState();
const [theme, setTheme] = useState('midnight');
const pgn = useMemo(() => new PGNRecorder(), []);
const handleMove = ({ from, to, fen }) => {
pgn.push({ from, to });
setFen(fen);
};
const exportGame = () => {
pgn.setHeaders({
Event: 'Online Game',
Site: 'My App',
Date: new Date().toISOString().slice(0, 10),
});
pgn.download();
};
return (
<div className="chess-game">
<div className="board-controls">
<button onClick={() => setTheme('classic')}>Classic</button>
<button onClick={() => setTheme('midnight')}>Midnight</button>
<button onClick={exportGame}>Export PGN</button>
</div>
<NeoChessBoard
theme={theme}
fen={fen}
onMove={handleMove}
showCoordinates
style={{ width: '100%', maxWidth: '500px' }}
/>
<div className="game-info">
<textarea value={pgn.getPGN()} readOnly />
</div>
</div>
);
}
Custom Themes
import { NeoChessBoard, THEMES, registerTheme } from '@magicolala/neo-chess-board';
// Extend existing theme
const customTheme = {
...THEMES.midnight,
moveFrom: 'rgba(255, 215, 0, 0.6)', // Golden highlight
moveTo: 'rgba(0, 255, 127, 0.4)', // Spring green
};
// Optionally register for later use
registerTheme('sunset', customTheme);
const board = new NeoChessBoard(element, {
// Apply directly with an object
theme: customTheme,
});
board.setTheme('sunset');
Integration with Chess.js
import { Chess } from 'chess.js';
import { NeoChessBoard, ChessJsRules } from '@magicolala/neo-chess-board';
const game = new Chess();
const rules = new ChessJsRules();
const board = new NeoChessBoard(element, {
rulesAdapter: rules,
onMove: ({ from, to }) => {
const move = game.move({ from, to });
if (move) {
rules.getPgnNotation().addMove(rules.moveNumber(), move.san);
// Add annotations to the last move
rules.getPgnNotation().addMoveAnnotations(rules.moveNumber(), true, {
arrows: [{ from: move.from, to: move.to, color: '#00ff00' }],
circles: [{ square: move.to, color: '#ffff00' }],
textComment: 'Good move!',
});
}
},
});
// To get the PGN with annotations from ChessJsRules:
const pgnWithAnnotations = rules.toPgn(true);
console.log(pgnWithAnnotations);
๐๏ธ Architecture
Neo-Chess-Board-Ts-Library/
โโโ ๐ฏ Core Engine
โ โโโ NeoChessBoard # Main board class
โ โโโ BoardDomManager # Canvas & DOM layout orchestration
โ โโโ BoardEventManager # Pointer + keyboard routing
โ โโโ BoardAudioManager # Move sound lifecycle
โ โโโ EventBus # Type-safe event system
โ โโโ LightRules # Built-in chess rules
โ โโโ Utils # Chess utilities
โโโ ๐จ Rendering
โ โโโ FlatSprites # SVG-like piece rendering
โ โโโ Themes # Visual theme system
โ โโโ Canvas Layers # Optimized rendering
โโโ โ๏ธ React Integration
โ โโโ NeoChessBoard # React component wrapper
โโโ ๐ PGN Support
โโโ PGNRecorder # Game notation recording
Internal helper managers
The refactored core is split into focused helpers so you can reason about behaviour changes quickly:
BoardDomManager
builds and maintains the layered canvas/DOM structure, applies inline styles, and wires resize logic.BoardEventManager
centralises pointer/keyboard routing so interaction tweaks stay isolated from rendering concerns.BoardAudioManager
owns move sound configuration, live toggles, and per-colour playback.
Each manager exposes a small API and is orchestrated by NeoChessBoard
, making it easier to customise or swap subsystems without touching unrelated code.
๐ฏ Why Neo Chess Board?
Feature | Neo Chess Board | Other Libraries |
---|---|---|
Bundle Size | ๐ข ~15kb | ๐ด 50-200kb |
TypeScript | ๐ข Full support | ๐ก Partial |
React Ready | ๐ข Native hooks | ๐ด Wrapper needed |
Performance | ๐ข Canvas optimized | ๐ก DOM heavy |
Themes | ๐ข Built-in + custom | ๐ด Limited |
PGN Export | ๐ข Included | ๐ด External dep |
Modern Code | ๐ข ES2022+ | ๐ด Legacy |
๐ API Reference
React Component Props
interface NeoChessProps {
// Position & Rules
fen?: string; // Position in FEN notation
rulesAdapter?: RulesAdapter; // Custom rules engine
// Visual Appearance
theme?: ThemeName | Theme; // Built-in theme name or custom object
orientation?: 'white' | 'black'; // Board flip
autoFlip?: boolean; // Automatically follow the side to move
showCoordinates?: boolean; // A-H, 1-8 labels (always bottom/left)
// Interaction
interactive?: boolean; // Enable piece dragging
highlightLegal?: boolean; // Show legal move dots
animationMs?: number; // Move animation speed (legacy alias)
animationDurationInMs?: number; // Preferred animation duration alias
showAnimations?: boolean; // Toggle move animations
allowDragging?: boolean; // Enable pointer dragging interactions
dragActivationDistance?: number; // Minimum pointer travel before dragging starts
allowDragOffBoard?: boolean; // Allow dropping outside the board to cancel
allowAutoScroll?: boolean; // Auto-scroll scrollable ancestors near edges
allowDrawingArrows?: boolean; // Enable right-click arrow drawing
clearArrowsOnClick?: boolean; // Clear arrows on left click
arrowOptions?: { color?: string; width?: number; opacity?: number }; // Default arrow styling
arrows?: Arrow[]; // Controlled arrows collection
onArrowsChange?: (arrows: Arrow[]) => void; // Change callback for arrows
canDragPiece?: (params: { square: Square; piece: string; board: NeoChessBoard }) => boolean; // Control drag eligibility
// Event Handlers
onMove?: (move: MoveEvent) => void;
onIllegal?: (attempt: IllegalMoveEvent) => void;
onUpdate?: (state: UpdateEvent) => void;
// Styling
style?: React.CSSProperties;
className?: string;
}
Core Board Methods
class NeoChessBoard {
constructor(element: HTMLElement, options?: BoardOptions);
// Position Management
getPosition(): string;
setPosition(fen: string, immediate?: boolean): void;
// Event System
on<T>(event: string, handler: (data: T) => void): () => void;
// Rendering
resize(): void;
renderAll(): void;
// Cleanup
destroy(): void;
}
๐งช Testing
Neo Chess Board ships with a Jest-based test environment that covers the core engine, React bindings, and demo scenarios. The suite lives under tests/
, and tests/README.md
describes the folder structure together with guidance for adding additional cases. Running the suite locally will also refresh helper artifacts such as tests/RESULTS.md
and the coverage output.
npm test # Run the full suite once
npm run test:watch # Re-run affected tests on file changes
npm run test:coverage # Produce an updated coverage summary + HTML report
โน๏ธ Coverage numbers depend on your latest local execution. After running the commands above you can open
coverage/lcov-report/index.html
for detailed metrics or commit an updated summary totests/RESULTS.md
if you capture a new campaign.
๐ Performance
- Smooth 60fps animations
- Optimized Canvas rendering with layers
- Efficient piece sprite system
- Minimal re-renders in React
- Memory efficient event system
๐ Examples Gallery
Check out these live examples powered by Neo Chess Board:
- ๐ Vanilla JS Starter โ Quick start board with theme switching, move history, and PGN export helpers.
- โ Chess.js Integration โ Demonstrates the ChessJsRules adapter synchronized with the chess.js engine.
- ๐ PGN + Evaluation HUD โ Import annotated games and follow the evaluation bar as you navigate.
- โก Advanced Features Showcase โ Explore puzzles, analysis tools, and keyboard-driven workflows.
๐ค Contributing
We love contributions! See CONTRIBUTING.md for details.
- ๐ Report bugs
- ๐ก Request features
- ๐ง Submit PRs
๐ License
MIT ยฉ Cรฉdric Oloa