โ™Ÿ๏ธ Chess Board Web Components

A collection of web components for displaying chess boards and pieces, built with TypeScript and Vite

npm version coverage lines coverage statements coverage functions coverage branches

โœจ Features

๐ŸŽฏ 8x8 Chess Board

Traditional chess board layout with alternating colors and coordinate labels

โ™Ÿ๏ธ Individual Chess Pieces

Separate component for each piece type and color, including fairy pieces

๐Ÿ”„ Piece Rotation

Rotate pieces in 45ยฐ increments (0-315ยฐ) with keyboard shortcuts or API

๐ŸŽจ Fairy Chess Notation

Support for fairy-name and fairy-condition annotations for problem compositions

๐Ÿ–ฑ๏ธ 3-Button Mouse Editing

Main click selects/moves, middle/auxiliary click removes pieces, and context click dispatches a dedicated event

๐Ÿงฌ Clone & Invert Editing

Move a selected piece while holding Shift to clone it, or Ctrl to clone and flip the color

โŒจ๏ธ Keyboard Navigation

Full keyboard support for piece placement, rotation, and board manipulation

๐Ÿ“ FEN Support

Load and display chess positions using Forsyth-Edwards Notation

โ†”๏ธ Board Orientation

Flip between white/black perspective with keyboard shortcuts

๐Ÿ“ฑ Responsive Design

Automatically adapts to container size with CSS Container Queries

โš™๏ธ Programmatic API

Complete TypeScript API for adding, removing, and rotating pieces

๐Ÿงฉ Cell Decorators

Add a visual overlay layer inside each square above the background and below pieces or notation

๐Ÿงช Fully Tested

Comprehensive test suite with Vitest (241 tests passing)

๐Ÿ”ง TypeScript

Full type safety and modern development experience

๐ŸŒ Web Components

Native custom elements with Shadow DOM for safe integration

๐Ÿ–ผ๏ธ Full html2canvas compatible

Fully compatible with html2canvas for capturing the board as an image

๐ŸŽฎ Live Demo

Try the interactive chess board below. Click to select a square, then use keyboard shortcuts to add and manipulate pieces:

๐Ÿ’ก Click on the board and try: Arrow keys (navigate), P/p (add pieces), Alt/Option+Arrow (rotate), Delete (remove)

๐Ÿ“ฆ Installation

pnpm add @dardino/chess-board

๐Ÿš€ Basic Usage

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chess Board Demo</title>
  
  <!-- Load chess piece font (required) -->
  <link rel="stylesheet" href="./assets/ScacchiPainter.css" />
</head>
<body>
  <!-- Empty chess board -->
  <chess-board></chess-board>

  <!-- Board with FEN position -->
  <chess-board fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"></chess-board>

  <script type="module">
    import '@dardino/chess-board';
  </script>
</body>
</html>

โš™๏ธ Programmatic API

The chess board provides a complete TypeScript API for programmatic manipulation:

Adding and Removing Pieces

const board = document.querySelector('chess-board');

// Add a piece
board.addPiece('e4', 'q', 'w');              // White queen on e4
board.addPiece('d4', 'k', 'b', '45');        // Black king on d4, rotated 45ยฐ

// Remove a piece
board.removePiece('e4');

// Check if square has piece
if (board.hasPiece('d4')) {
  console.log('Square d4 is occupied');
}

// Get piece information
const piece = board.getPieceAt('d4');
console.log(piece); // { type: 'k', color: 'b', rotation: '45' }

Bulk Operations

// Get all pieces on board
const pieces = board.getAllPieces();
console.log(pieces);
// [{ square: 'd4', type: 'k', color: 'b', rotation: '45' }, ...]

// Set multiple pieces at once (clears board first)
board.setPieces([
  { square: 'e1', type: 'k', color: 'w', rotation: '0' },
  { square: 'e8', type: 'k', color: 'b', rotation: '0' },
  { square: 'd1', type: 'q', color: 'w', rotation: '45' }
]);

Piece Rotation

// Rotate piece by relative amount
board.rotatePiece('e4', 45);                 // Rotate clockwise by 45ยฐ
board.rotatePiece('e4', -45);                // Rotate counter-clockwise

// Set absolute rotation
board.setPieceRotation('e4', '180');         // Set to 180ยฐ

// Get rotation
const rotation = board.getPieceRotation('e4');
console.log(rotation); // '180'

Cell Decorators

// Add a visual decorator layer under pieces but above the square background
board.setCellDecorators({
  e4: { backgroundColor: '#ffeb3b', innerBorder: 'solid 2px #d97706' },
  d4: { backgroundColor: 'rgba(59, 130, 246, 0.25)', innerBorder: 'solid 1px rgba(37, 99, 235, 0.8)' }
});

// Clear all decorators
board.setCellDecorators({});

Piece Selection & Movement

// Get the currently selected piece square
const selectedSquare = board.getSelectedPieceSquare();
if (selectedSquare) {
  console.log(`Piece selected at: ${selectedSquare}`);
  console.log(`Piece info:`, board.getPieceAt(selectedSquare));
}

// Select the piece on e4 and make e4 the current square
const wasSelected = board.selectPiece('e4');
console.log(wasSelected); // true when e4 contains a piece

// Mouse workflow:
// 1. Primary click selects/moves pieces
// 2. Auxiliary (middle) click removes any piece on the clicked square
// 3. Hold Shift while moving to clone the piece
// 4. Hold Ctrl while moving to clone and invert the piece color
// 5. Keyboard workflow (Space or Enter):
//    a. Press Space or Enter on a square with a piece โ†’ selects it
//    b. Navigate with arrow keys to an empty square
//    c. Press Space or Enter again โ†’ moves the piece there and clears selection
//    d. Press Escape โ†’ cancels selection without moving
//    e. Any other key or blur event โ†’ clears selection

Current Square

// Get the current keyboard-navigation square
const currentSquare = board.getCurrentSquare(); // string | null

// Set the current square without selecting a piece
board.selectSquare('e4');

Events

// Preferred event name for the main mouse button
board.addEventListener('cellMainClick', (event) => {
  const { square, piece } = event.detail;
  console.log('Clicked:', square, piece);
});

// Deprecated compatibility alias for older code
board.addEventListener('cellClick', (event) => {
  const { square, piece } = event.detail;
  console.log('Legacy click:', square, piece);
});

board.addEventListener('fenChange', (event) => {
  console.log('Current FEN/FFEN:', event.detail.fen);
});

Cell Decorators

<chess-board id="sample-board" fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"></chess-board>

<script type="module">
  const board = document.getElementById('sample-board');

  board.addPiece('e4', 'q', 'w');
  board.addPiece('d4', 'k', 'b');

  board.setCellDecorators({
    e4: {
      backgroundColor: '#ffeb3b',
      innerBorder: 'solid 2px #d97706'
    },
    d4: {
      backgroundColor: 'rgba(59, 130, 246, 0.25)',
      innerBorder: 'solid 1px rgba(37, 99, 235, 0.8)'
    }
  });

  board.addEventListener('cellClick', ({ detail }) => {
    if (detail.square === 'e4') {
      board.setCellDecorators({
        e4: { backgroundColor: '#fca5a5', innerBorder: 'solid 2px #dc2626' }
      });
    }
  });
</script>

Board Orientation

// Set orientation
board.setOrientation('white');               // White at bottom
board.setOrientation('black');               // Black at bottom

// Get orientation
const orientation = board.getOrientation();  // 'white' | 'black'

// Toggle orientation
board.toggleOrientation();

Element Attributes

chess-board supports the boolean disabled and auto-select-piece-on-click attributes, along with fen and the boolean hide-labels attribute. Its black-to-move attribute is managed by the orientation API and by the active color in FEN.

chess-piece supports piece, color, rotation, fairy-name, and fairy-condition. Fairy names are limited to three characters.

fen

<chess-board fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"></chess-board>

const board = document.querySelector('chess-board');
board.setFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
const fen = board.getFen();

Use the fen attribute or the setFen() API to control the board position. It accepts both standard FEN and FFEN values.

hide-labels

<chess-board hide-labels></chess-board>

const board = document.querySelector('chess-board');
board.hideLabels = true;
board.hideLabels = false;

When enabled, the board hides square labels such as file and rank markers for a cleaner visual presentation.

disabled

<chess-board disabled></chess-board>

const board = document.querySelector('chess-board');
board.disabled = true;
board.disabled = false;

When set, the board becomes non-interactive: pointer events and keyboard actions are disabled, and selections cannot change until the attribute is removed or set to false.

auto-select-piece-on-click

<chess-board auto-select-piece-on-click></chess-board>

const board = document.querySelector('chess-board');
board.autoSelectPieceOnClick = true;
board.autoSelectPieceOnClick = false;

When enabled, clicking a piece automatically selects it, which is helpful for one-click move workflows. The attribute can be toggled through the DOM or through the autoSelectPieceOnClick property.

FEN Support

// Set position from FEN
board.setFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');

// Get current FEN
const fen = board.getFen();

// Preset positions
board.setStartingPosition();                 // Standard starting position
board.clearBoard();                          // Empty board

๐Ÿงฉ FFEN support

The board accepts a single fen attribute or setFen() call for either standard FEN or FFEN. The component automatically detects the format and preserves fairy metadata when it is present.

Supported FFEN features

FFEN examples

// Standard FEN
board.setFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');

// Neutral piece example
board.setFen('8/8/8/8/8/8/8/-K6 w - - 0 1');

// 90ยฐ rotation example
board.setFen('8/8/8/8/4*1B3/8/8/8 w - - 0 1');

// Fairy metadata example
board.setFen('8/8/8/8/8/8/8/4K3 w - - 0 1 e4:gn:Chameleon');

const currentState = board.getFen();
console.log(currentState);

In short, FFEN is handled as the same notation family as FEN: the same fen attribute and the same setFen() API are used, and the component automatically applies the correct parsing logic based on the value passed in.

โŒจ๏ธ Keyboard Navigation

Keys Action
Arrow Keys Navigate between squares
Space / Enter Select, deselect, or move the piece on the current square
Shift + move Clone the selected piece while moving it
Ctrl + move Clone the selected piece and invert its color
P/R/N/B/Q/K Add white piece (uppercase)
p/r/n/b/q/k Add black piece (lowercase)
E/e, T/t, A/a Add fairy pieces (Empress, Amazon, Archbishop)
C/c, S/s, X/x Add Circle (C/c) or Square (S/s) or Cross (X/x)
Alt/Option + โ†/โ†’ Rotate piece ยฑ45ยฐ
Alt/Option + โ†‘/โ†“ Reset rotation to 0ยฐ / Set to 180ยฐ
Shift + โ†‘/โ†“ Flip board to white/black perspective
Delete/Backspace Remove piece from current square
Escape Clear all pieces
Shift + Escape Reset to starting position

๐Ÿ”ง TypeScript Support

Full TypeScript support with comprehensive type definitions:

import {
  ChessBoard,
  type PieceInfo,
  type PieceInfoWithSquare,
  type ChessPieceType,
  type ChessPieceColor,
  type ChessPieceRotation
} from '@dardino/chess-board';

// Type-safe piece manipulation
const board = document.querySelector('chess-board') as ChessBoard;

function addPiece(
  square: string,
  type: ChessPieceType,
  color: ChessPieceColor,
  rotation?: ChessPieceRotation
): void {
  try {
    board.addPiece(square, type, color, rotation);
  } catch (error) {
    console.error(`Failed to add piece: ${error.message}`);
  }
}

// Type-safe piece information
const pieces: PieceInfoWithSquare[] = board.getAllPieces();
pieces.forEach(piece => {
  console.log(`${piece.color} ${piece.type} on ${piece.square}`);
});

Available Types

Type Description
ChessPieceType 'k' | 'q' | 'r' | 'b' | 'n' | 'p' | 'e' | 't' | 'a' | 'x' | 's' | 'c' | `${number}` | `'${string}` | `''${string}`
ChessPieceColor 'w' | 'b' | 'n'
ChessPieceRotation '0' | '45' | '90' | '135' | '180' | '225' | '270' | '315'
PieceInfo { type, color, rotation, fairyName, fairyCondition }
PieceInfoWithSquare PieceInfo plus square: string; returned by getAllPieces()
FenPosition { pieces, activeColor, castlingRights, enPassantTarget, halfmoveClock, fullmoveNumber }
CellDecorator { backgroundColor: string, innerBorder: string }

๐ŸŽฏ Chess Pieces

Standard Pieces

Piece Type Description
King k King piece
Queen q Queen piece
Rook r Rook (castle)
Bishop b Bishop
Knight n Knight (horse)
Pawn p Pawn

Fairy Pieces

Piece Type Description
Empress e Rook + Knight
Amazon t Queen + Knight
Angel/Archbishop a Bishop + Knight

Symbolic Pieces

Piece Type Description
Circle c
Square s
Cross x

Characters Pieces

Piece Type Description
Fairy letter or number Examples: 'A with color w, 'B with color b, or '7 Writes the character on the board
Writes the character on the board
Two-character fairy number Example: ''23 Writes on the board. Programmatic piece types may also use a single apostrophe followed by multiple alphanumeric characters.
Notes: Using letters or numbers in FFEN results in black fairy pieces by default. FFEN cannot distinguish white and black numbers, so use the Programmatic API when a character piece needs a different color.

โš™๏ธ Other available APIs

This library exposes several APIs for interacting with the chess board programmatically. This list is not exhaustive, and more APIs may be available.

parseFen

Function to parse a FEN or FFEN string into a FenPosition object

import { parseFen } from '@dardino/chess-board'
function parseFen(fen: string): FenPosition | null
// example usage:
const fenString = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
const parsedPosition = parseFen(fenString);

positionToFen

Converts a FenPosition to FFEN, preserving fairy metadata and normalizing metadata values.

import { positionToFen } from '@dardino/chess-board'
      function positionToFen(position: FenPosition): string

      

Piece-placement utilities

import {
  parsePiecePlacement,
  piecesToFenString,
  parseFfenPieceChar,
  type PieceInfo,
  type PiecesOnBoard,
  type ChessPieceType,
  type ChessPieceColor
} from '@dardino/chess-board';

function parsePiecePlacement(piecePlacement: string): {
  pieces: PiecesOnBoard;
  boardSize: { width: number; height: number };
} | null

function piecesToFenString(
  pieces: PiecesOnBoard,
  isFfen?: boolean,
  boardSize?: { width: number; height: number }
): string

function parseFfenPieceChar(piece: string): {
  type: ChessPieceType;
  color: ChessPieceColor | 'n';
  isNeutral: boolean;
  rotation?: number;
  fairyName?: string;
} | null

๐Ÿงช Testing

Comprehensive test suite with 241 tests passing

pnpm test

๐Ÿ“„ License

MIT License - See LICENSE file for details

๐Ÿ”— Links