npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

chessboard-react

v0.9.0

Published

Échiquier React contrôlé : coups au pointeur, flèches et cercles d'annotation, promotion. Sans état interne, sans design system imposé.

Readme

chessboard-react

Échiquier React contrôlé : il affiche une position, signale les coups et les flèches, et ne décide de rien. La partie, la navigation dans les coups et les boutons appartiennent à l'application.

pnpm add chessboard-react chess.js

react et chess.js sont des dépendances peer : c'est votre version qui est utilisée, jamais une seconde copie.

Utilisation

import { useState } from 'react'
import { Chess } from 'chess.js'
import { ChessBoard, cburnett } from 'chessboard-react'
import 'chessboard-react/styles.css'

export function Board() {
  const [fen, setFen] = useState('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1')

  return (
    <ChessBoard
      fen={fen}
      pieces={cburnett}
      onMove={move => {
        const game = new Chess(fen)
        game.move(move)
        setFen(game.fen())
      }}
    />
  )
}

onMove notifie, il ne joue pas. Le composant ne garde aucune copie de la position : tant que vous ne repassez pas une nouvelle fen, rien ne bouge à l'écran. Une seule source de vérité, la vôtre.

Les positions incomplètes des livres (sans roi, ou avec un seul) restent jouables : l'échiquier charge chess.js avec skipValidation. Les règles tiennent toujours — un roi en échec doit parer, et un camp sans roi n'est jamais en échec ni mat. Pour appliquer ces coups, faites de même : new Chess(fen, { skipValidation: true }). useChessGame le fait déjà.

Navigation dans une partie

L'échiquier ne sait afficher qu'une position. Pour parcourir les coups d'une partie, le hook useChessGame s'en charge — séparément, parce que les applications qui affichent une position sans historique (un diagramme, une position issue d'une détection d'image) n'en ont pas besoin.

const game = useChessGame({ pgn })

<ChessBoard
  fen={game.fen}
  pieces={cburnett}
  orientation={game.orientation}
  lastMove={game.lastMove}
  onMove={game.play}
/>
<button onClick={game.prev} disabled={!game.canPrev}>Précédent</button>
<button onClick={game.next} disabled={!game.canNext}>Suivant</button>
<button onClick={game.flip}>Retourner</button>

Props

| Prop | Défaut | Rôle | | --- | --- | --- | | fen | — | Position affichée. Seule source de vérité. | | pieces | — | Images des pièces. Obligatoire, voir plus bas. | | orientation | 'white' | Camp affiché en bas. Prop pure : la changer retourne l'échiquier. | | moves | 'play' | 'none' fige la position. Avec annotations="none", l'échiquier n'est plus qu'une image. | | annotations | 'none' | 'draw' autorise le tracé de flèches et de cercles. | | arrows | [] | Flèches et cercles affichés — toujours honorés, même avec annotations="none". | | onArrowsChange | — | Le tracé remonte ici. Les flèches vous appartiennent. | | dividers | [] | Traits qui partagent l'échiquier en plusieurs positions, voir plus bas. | | onMove | — | Coup légal joué au pointeur. À vous de l'appliquer. | | lastMove | — | { from, to } du dernier coup, surligné dans sa propre teinte. | | clearArrowsOn | 'interaction' | 'move', 'interaction' ou 'never'. | | showCoordinates | true | Lettres et chiffres dans les coins. | | coordinateSize | 16 | Taille des coordonnées, en pourcentage du côté d'une case (8 à 40). | | moveSound | 'wood' | 'wood', 'alabaster', une URL/data URI personnalisée, ou false. | | labels | français | Tous les textes visibles et accessibles. |

Le survol et le curseur main ne sont posés que sur les cases qui répondent au clic : les pièces au trait — y compris une pièce choisie, pour en changer — et les destinations légales de la pièce choisie. Une case vide sans rapport ou une pièce adverse ne s'allume jamais : l'échiquier ne promet rien qu'il ne tienne. Au doigt, rien de tout ça n'apparaît (@media (hover: hover)).

Flèches et cercles

Un cercle est une flèche d'une case vers elle-même ({ from: 'e4', to: 'e4' }), comme dans chessground : %cal et %csl du PGN tiennent dans le même tableau, et tout ce qui transporte vos flèches transporte aussi les cercles.

Avec annotations="draw" :

| Geste | Souris | Doigt | | --- | --- | --- | | Symbole (cercle par défaut) | clic droit sur une case | double tap sur la case | | Flèche | clic droit glissé | double tap, le doigt maintenu puis glissé | | Couleur | touches tenues pendant le clic droit ; trois clics droits rapprochés : couleur suivante | triple tap : couleur suivante | | Palette (couleurs et symboles) | clic droit maintenu une demi-seconde sur une case, ou trois clics droits dont le dernier maintenu : glisser jusqu'à l'option et relâcher, ou relâcher ailleurs puis cliquer | doigt maintenu une demi-seconde sans bouger (même sur une pièce), ou triple tap maintenu ; même principe | | Tout effacer | clic gauche sur une case vide (clearArrowsOn), ou quatre clics droits rapprochés | quadruple tap, ou bouton de la palette |

Les couleurs à la souris reprennent les raccourcis de lichess : sans touche la couleur choisie (green au départ), Shift (ou Ctrl) red, Alt (ou Meta, ou AltGr) blue, les deux yellow. Retracer une annotation identique l'efface ; sinon elle remplace celle de la case. La couleur et le symbole choisis restent actifs jusqu'au prochain changement. Tout se fait d'une seule main.

Une annotation sur une seule case accepte un shape, pour reproduire les diagrammes des livres : circle (défaut), dot (rond), diamond (losange), square (carré), frame (case encadrée), plus et minus.

<ChessBoard fen={fen} pieces={cburnett} arrows={[{ from: 'c6', to: 'c6', shape: 'plus' }, { from: 'e4', to: 'e4', shape: 'diamond', color: 'blue' }]} />

Afficher et dessiner sont deux choses distinctes : un moteur peut suggérer des flèches sur un échiquier où l'utilisateur ne peut ni jouer ni annoter.

<ChessBoard fen={fen} pieces={cburnett} moves="none" annotations="none" arrows={engineLines} />

Avec moves="none" annotations="none", aucun gestionnaire de pointeur n'est posé et touch-action reste libre : la page défile normalement au doigt au-dessus de l'échiquier, et le menu contextuel reste disponible.

Les pièces, et leur licence

pieces est obligatoire et sans valeur par défaut. C'est délibéré : un jeu par défaut se retrouverait dans le bundle de tous les consommateurs, y compris ceux qui fournissent le leur — avec les obligations de licence qui vont avec.

Le jeu fourni, cburnett, est celui de Wikipédia. Il est redistribué sous licence BSD 3-clauses, qui impose de reproduire son avis de copyright dans la documentation du produit qui le diffuse. Trois choses le rendent difficile à perdre en route :

  1. Le module porte un commentaire /*! … */, préservé par esbuild, terser et webpack jusque dans un bundle de production minifié.
  2. THIRD-PARTY-NOTICES.md est publié avec le paquet.
  3. pieceCredits expose la mention pour l'afficher depuis votre interface :
import { pieceCredits } from 'chessboard-react'

<ul>
  {pieceCredits.map(credit => (
    <li key={credit.id}>
      {credit.notice} <a href={credit.sourceUrl}>Source</a>
    </li>
  ))}
</ul>

Rendre ça sur une page de crédits ou dans vos mentions légales satisfait l'obligation. Un consommateur qui fournit son propre jeu n'importe jamais cburnett et n'est donc concerné par rien de tout ceci.

Son des déplacements

wood (« Bois feutré ») est joué par défaut chaque fois que la disposition des pièces dans fen change. Le son confirme donc la position contrôlée : si le parent refuse un coup signalé par onMove et conserve la même FEN, rien ne retentit.

// Les deux sons CC0 inclus :
<ChessBoard fen={fen} pieces={cburnett} moveSound="wood" />
<ChessBoard fen={fen} pieces={cburnett} moveSound="alabaster" />

// Couper le son ou fournir le sien :
<ChessBoard fen={fen} pieces={cburnett} moveSound={false} />
<ChessBoard fen={fen} pieces={cburnett} moveSound="/sounds/move.mp3" />

Les deux extraits sont embarqués en data URI : aucune ressource à copier dans public/, aucune requête réseau. Ils sont publiés sous CC0 1.0, donc utilisables et redistribuables commercialement sans attribution obligatoire. Le paquet conserve tout de même leur provenance dans THIRD-PARTY-NOTICES.md et via l'export soundCredits.

Traits de séparation

Les livres d'échecs montrent souvent deux positions sur un seul diagramme, séparées par un trait : « à gauche, les Blancs jouent… ; à droite… ». Chaque trait suit les bords des cases, d'un coin du quadrillage au suivant. Un coin s'écrit [colonnes, rangées], compté de 0 à 8 depuis le coin inférieur gauche de a1 :

// Entre les colonnes d et e, de haut en bas
<ChessBoard fen={fen} pieces={cburnett} dividers={[{ points: [[4, 0], [4, 8]] }]} />

// Entre les rangées 4 et 5
dividers={[{ points: [[0, 4], [8, 4]] }]}

// En escalier : entre d et e jusqu'à la 6e rangée, puis entre e et f
dividers={[{ points: [[4, 8], [4, 5], [5, 5], [5, 0]] }]}

Les traits suivent l'orientation et sont tracés sous les pièces. Ils ne changent rien au jeu : l'échiquier ne sait pas qu'il montre deux positions. Leur couleur et leur épaisseur passent par --board-divider (la couleur du cadre par défaut) et --board-divider-width (2px).

Thème

Toutes les couleurs passent par des variables CSS à valeur de repli — la feuille de style est correcte sans qu'aucune ne soit définie.

.mon-echiquier {
  --board-light: #e7efe9;
  --board-dark: #08643f;
  --board-frame: #06301f;
  --board-arrow-green: #d7a622;
  --board-highlight: rgb(215 166 34 / 34%);
}

Les autres : --board-frame-width, --board-shadow, --board-highlight-strong, --board-legal-dot, --board-coordinate-light, --board-coordinate-dark, --board-arrow-width, --board-arrow-red, --board-arrow-blue, --board-arrow-yellow, --board-circle-width, --board-backdrop, --board-dialog-bg, --board-dialog-ink, --board-dialog-muted, --board-radius, --board-radius-sm, --board-accent, --board-focus-ring, --board-light-hover, --board-last-move, --board-hover-light, --board-hover-dark.

La taille des coordonnées suit automatiquement celle de l'échiquier. Elle se règle avec coordinateSize, en pourcentage du côté d'une case.

Rendu

L'échiquier est dessiné dans un seul SVG : cases, surbrillances, pièces, coordonnées et cadre partagent un même repère. Le navigateur cale chaque boîte CSS sur le pixel — à l'impression surtout, où le pixel CSS mesure 0,26 mm —, si bien qu'une grille de cases en HTML n'a pas toutes ses colonnes de la même largeur et que les pièces semblent glisser. Dans un SVG, rien n'est arrondi : un diagramme imprimé reste net et régulier.

Quand l'échiquier est interactif, une grille de boutons transparents est posée par-dessus le dessin. Elle porte le focus au clavier, le nom des cases pour les lecteurs d'écran (role="gridcell", « f4 Cavalier blanc ») et la cible des gestes. Figé (moves="none" annotations="none"), l'échiquier n'a pas de boutons : c'est une image (role="img") nommée par labels.board.

Pour styler le dessin, visez ses classes SVG : .board-square--light, .board-square--dark, .board-highlight--selected, .board-highlight--last-move, .board-pieces (dont l'ombre portée se retire avec filter: none, ce que la feuille fait d'elle-même à l'impression), .rank-label, .file-label, .legal-move-dot. Les boutons .square gardent les classes d'état (square--selected, square--legal, square--playable…) mais ne dessinent rien.

Avant la 0.5, les cases étaient des boutons qui portaient le dessin : les règles qui visaient .square small, .square--light ou une img.piece ne s'appliquent plus, et --board-coordinate-size a disparu au profit de coordinateSize.

Les quatre emplacements de couleur s'appellent green, red, blue et yellow parce que ce sont les codes couleur du PGN. Ce sont des noms d'emplacement, pas des couleurs : chacun se redéfinit par sa variable. Avant la 0.4, green était doré par défaut ; ce doré est désormais celui de yellow.

Développement

pnpm install
pnpm test          # vitest + jsdom
pnpm run typecheck
pnpm run build     # tsup → dist/

Pour itérer depuis un projet consommateur sans republier :

pnpm add link:../chessboard-react

Publier

La publication est déclenchée par un tag :

pnpm version patch
git push --follow-tags

Le workflow .github/workflows/publish.yml rejoue types, tests et build avant pnpm publish. Il attend le secret NPM_TOKEN (jeton « Automation » créé sur npmjs.com).

Licence

MIT pour le code — voir LICENSE. BSD 3-clauses pour les pièces Cburnett et CC0 1.0 pour les sons — voir THIRD-PARTY-NOTICES.md.