cm-chessboard

repository·master·Indexed 43 days ago

https://github.com/shaack/cm-chessboard

A lightweight, dependency-free, SVG-rendered JavaScript chessboard library (v8.14.0) based on ES6 modules. It features responsive design, FEN position management, and a flexible extension system for adding functionality like markers, arrows, piece rotation, and accessibility support for visually impaired users.

Tokens
14.1K
Snippets
47
Records
58
Agent score
54%

What's inside cm-chessboard

  1. Integrate cm-chessboard with JS Frameworks

    master

    cm-chessboard is written in standard ES6 and has no dependencies, making it compatible with most modern environments.

    • Vue: Works out of the box.
    • Svelte: Works out of the box.
    • React: While not officially supported, community usage is documented in the repository issues.
    • Other Frameworks: Should work with any framework that supports standard ES6 modules.
  2. How extensions work in cm-chessboard

    master

    The core of cm-chessboard is kept minimal and fast. To add extra functionality, you use an extension system. Extensions are passed as an array of objects to the extensions property in the Chessboard configuration. Each object should contain a class property referencing the extension class.

    Available Extensions:

    • RightClickAnnotator: Uses Markers and Arrows to handle mouse events for drawing on the board.
    • Markers: Creates markers on specific squares.
    • Arrows: Renders arrows on the chessboard.
    • Accessibility: Improves board accessibility.
    • PromotionDialog: Shows a dialog to select a piece during promotion.
  3. Extend functionality using the `Extension` class

    master

    Extensions allow you to hook into the chessboard lifecycle. An extension must extend the Extension class and can register for specific EXTENSION_POINT events.

    Available Extension Points:

    • positionChanged: Piece positions changed.
    • boardChanged: Board orientation changed.
    • moveInputToggled: Move input enabled/disabled.
    • moveInput: Move lifecycle (start, moving, validating, cancel).
    • beforeRedrawBoard / afterRedrawBoard: Around board redraws.
    • animation: Animation lifecycle (start, end, frames).
    • destroy: Before the board is destroyed.

    How to add an extension:

    1. Define a class extending Extension.
    2. Use this.registerExtensionPoint(name, callback) in the constructor.
    3. Pass the extension class in the Chessboard constructor props.
    class MyCoolChessboardExtension extends Extension {
        constructor(chessboard, props) {
            super(chessboard, props)
            this.registerExtensionPoint(EXTENSION_POINT.moveInput, (data) => {
                console.log(data)
            })
        }
    }
    
    const chessboard = new Chessboard(document.getElementById("board"), {
        extensions: [{
            class: MyCoolChessboardExtension,
            props: { /* config */ }
        }]
    })
  4. How PieceRotation works internally

    master

    The extension applies an SVGTransform to each piece's use element. This transform is appended after the standard translate and scale, allowing it to rotate in sprite coordinates around the tile center.

    Key behaviors:

    • Persistence: A MutationObserver re-applies angles to new pieces, ensuring rotation survives redraws and position changes.
    • Independence: Rotation is independent of move input. The dragged ghost piece following the pointer is not rotated.
  5. How marker type identity works

    master

    Marker types are matched by object reference, not by structural equality. This means that even if two marker type objects have identical properties, they are treated as different types if they are not the exact same object in memory.

    Crucial Rules:

    1. Use Constants: Always define your marker types as module-level constants.
    2. Reuse References: Pass the exact same reference to addMarker and removeMarkers.
    3. Avoid Cloning: Do not clone marker types (e.g., via JSON.parse(JSON.stringify(type)) or by storing them in a framework state that performs deep-copying), as the reference will break and removeMarkers will fail to find a match.

    If you use the built-in MARKER_TYPE constants, import them once and reuse that specific reference.

    // ✅ CORRECT: Using the same reference
    const myType = {class: "marker-frame", slice: "markerFrame"};
    board.addMarker(myType, "e4");
    board.removeMarkers(myType); // Successfully removed
    
    // ❌ INCORRECT: Different references, even if they look identical
    board.addMarker({class: "marker-frame", slice: "markerFrame"}, "e4");
    board.removeMarkers({class: "marker-frame", slice: "markerFrame"}); // Silently does nothing
  6. Use custom piece sets

    master

    cm-chessboard supports custom piece sets via SVG sprites.

    Requirements for custom sprites:

    • Must be a single SVG sprite.
    • Size: 40x40px.
    • Piece elements must have specific IDs, for example: bp (black pawn), wq (white queen).

    Two sets are included by default: staunty and Wikimedia standard.

  7. Enable user move input

    master

    By default, the chessboard is read-only. To allow users to move pieces via click or drag, you must:

    1. Use an extension: It is recommended to use the Markers extension to improve the visual experience of moving pieces.
    2. Call enableMoveInput(handler): Pass a callback function that handles move events.
    3. Handle events: The handler receives an event object. Returning true allows the move, while returning false cancels it. Use INPUT_EVENT_TYPE to check the event state.
    import {Chessboard, FEN, INPUT_EVENT_TYPE} from "./path/to/Chessboard.js"
    import {Markers} from "./path/to/extensions/markers/Markers.js"
    
    const board = new Chessboard(document.getElementById("board"), {
        position: FEN.start,
        assetsUrl: "../assets/",
        extensions: [{class: Markers}]
    })
    
    board.enableMoveInput(inputHandler)
    
    function inputHandler(event) {
        console.log(event)
        if(event.type === INPUT_EVENT_TYPE.moveInputStarted || 
           event.type === INPUT_EVENT_TYPE.validateMoveInput) {
            return true // true allows the move, false cancels it
        }
    }
  8. Create custom markers

    master

    You can create custom markers by defining an object that maps a CSS class for styling to an SVG slice ID for the visual shape.

    Marker Object Schema

    An object must contain:

    • class: The CSS class used to style the marker (e.g., setting stroke, opacity, etc.).
    • slice: The id of the <g> element within sprite.svg that defines the marker's shape.

    Implementation Example

    To create a red circle marker, you would define a CSS class .marker-circle-red and use the existing markerCircle slice from the SVG.

    /* Define the visual style */
    marker.marker-circle-red {
        stroke: #aa0000;
        stroke-width: 3px;
        opacity: 0.4;
    }
    // Define the custom marker type
    const myMarkerType = { class: "marker-circle-red", slice: "markerCircle" };
    
    // Use it on the board
    chessboard.addMarker(myMarkerType, "e4");
    
    // Remove specific instances
    chessboard.removeMarkers(myMarkerType, "e4");
    
    // Remove all markers of this custom type
    chessboard.removeMarkers(myMarkerType);
    const myMarkerType = {class: "marker-circle-red", slice: "markerCircle"}
    // add
    chessboard.addMarker(myMarkerType, "e4")
    // remove a specific marker
    chessboard.removeMarkers(myMarkerType, "e4")
    // remove all "myMarkerType"
    chessboard.removeMarkers(myMarkerType)
    // remove all markers
    chessboard.removeMarkers()
  9. Create a basic cm-chessboard instance

    master

    To render a chessboard, follow these steps:

    1. Include the CSS: Link to the project's CSS file. `<link rel="stylesheet" href="./node_modules/cm-chessboard/assets/chessboard.css">

    2. **Add a container**: Create adivin your HTML to hold the board. <div id="board"></div>3. **Initialize in JavaScript**: ImportChessboardandFEN, then instantiate the board. You **must** provide an assetsUrlpointing to the project'sassets` folder (containing piece SVGs and resources).

    <link rel="stylesheet" href="./node_modules/cm-chessboard/assets/chessboard.css">
    
    <div id="board"></div>
    
    <script type="module">
      import {Chessboard, FEN} from "./path/to/Chessboard.js"
    
      const board = new Chessboard(document.getElementById("board"), {
        position: FEN.start,
        assetsUrl: "./path/to/assets/"
      })
    </script>
  10. Install cm-chessboard

    master

    You can install cm-chessboard using one of the following three methods:

    1. npm: npm install cm-chessboard
    2. GitHub: Download the source code directly from the repository.
    3. CDN: Use the JSDelivr link: https://cdn.jsdelivr.net/npm/cm-chessboard@8/src/Chessboard.js
    npm install cm-chessboard
  11. Install and use the PieceRotation extension

    master

    The PieceRotation extension allows you to rotate piece glyphs in place by any angle, either for all pieces or for a specific color. This is useful for over-the-board play where pieces need to be oriented towards different players.

    To use it, import PieceRotation and add it to the extensions array in your Chessboard configuration object.

    import {PieceRotation} from "cm-chessboard/src/extensions/piece-rotation/PieceRotation.js"
    
    const board = new Chessboard(context, {
        position: FEN.start,
        assetsUrl: "./assets/",
        extensions: [{class: PieceRotation, props: {animationDuration: 300}}]
    })
  12. Run unit tests

    master

    The project uses the Teevi framework for unit testing. Tests run in a real browser.

    • In the browser: Open test/index.html directly or run npm test.
    • Headless (CI): Run npm run test:headless. This requires puppeteer to be installed globally because it is not a project dependency.

    To install the required headless dependency:

    npm install -g puppeteer