Crosswords
Fill-in crossword puzzles with clues and per-word validation
Crosswords renders an interactive crossword grid where users fill in answers. Each word is validated against a legend with clue and solution. Position, direction, and start coordinates are derived by matching solutions to words in the grid.
Import
import { Crosswords } from '@bside-tech/tbx-ui/components';Example
| 1 | 2 | |||||
| 3 | ||||||
| 4 |
Across
- 1. A primary color
- 3. Bits and bobs
- 4. Conclusions
Down
- 1. Toy on a string
- 2. Have a rest
<Crosswords
grid={[
["Y", "E", "L", "L", "O", "W", ""],
["O", " ", "I", " ", " ", " ", " "],
["Y", "", "E", "", "", "", ""],
["O", "D", "D", "S", "A", "N", "D"],
["", "", "O", "", "", "", ""],
["", "", "W", "", "", "", ""],
["", "E", "N", "D", "S", "", ""],
]}
legend={[
{ clue: "A primary color", solution: "YELLOW" },
{ clue: "Toy on a string", solution: "YOYO" },
{ clue: "Have a rest", solution: "LIEDOWN" },
{ clue: "Bits and bobs", solution: "ODDSAND" },
{ clue: "Conclusions", solution: "ENDS" },
]}
/>Grid format
The grid is a 2D array of strings. Each cell is either:
- Letter — Fillable cell (user types the answer).
''or' '— Blocked cell (black square in a crossword).
Legend format
Each legend entry has only clue and solution. Position, direction, and start coordinates are derived by matching each solution to words in the grid (in reading order: left-to-right, top-to-bottom).
| Field | Type | Description |
|---|---|---|
clue | string | The clue text shown to the user |
solution | string | The correct answer (must match a word in the grid exactly) |
Props
Prop
Type
Labels
Override labels via the labels prop. Supported keys: across, down (section headings), check, reset (buttons), results (e.g. "correct" in "3 / 5 correct"). No exercise label.
<Crosswords
grid={grid}
legend={legend}
labels={{
across: "Orizzontale",
down: "Verticale",
check: "Verifica",
reset: "Ripristina",
results: "corretti",
}}
/>Style
Tailwind v4 is available for customisations. Pass classes via the classNames prop, or override CSS variables for fine-grained control.
CSS Classes
| Key | Element |
|---|---|
root | Root wrapper |
grid | Grid container |
cell | Grid cell (blocked or input) |
legend | Legend container |
legendSection | Across/Down section |
legendItem | Clue list item |
checkButton | Check button |
resetButton | Reset button |
result | Results text |
CSS Variables
Scoped under .tbx-crosswords:
| Variable | Purpose |
|---|---|
--crosswords-cell-size | Cell width and height (default 2rem; overridden by cellSize prop) |
--crosswords-cell-bg | Fillable cell background (default) |
--crosswords-cell-border | Cell border |
--crosswords-cell-filled-bg | Cell background when filled (before validation). Uses color-mix with --exercise-accent-color. |
--crosswords-cell-filled-border | Cell border when filled (before validation). Uses --exercise-accent-color. |
--crosswords-cell-number | Clue number color (top-left of word-start cells) |
--crosswords-cell-text | Cell text color |
--crosswords-cell-correct | Cell background when correct |
--crosswords-cell-wrong | Cell background when wrong |
--crosswords-legend-bg | Legend background |
--exercise-action-bg, --exercise-action-border | Check/Reset buttons |
--exercise-result-neutral, --exercise-result-correct, --exercise-result-wrong | Results text |
Hook
Use useCompareGrid when building custom crossword interfaces:
import { useCompareGrid } from "@bside-tech/tbx-ui/hooks";
import type { CrosswordsLegendEntryInput } from "@bside-tech/tbx-ui/hooks";
const { grid, setCell, wordResults, isCorrect, check, reset, resolvedLegend, getLegendId } =
useCompareGrid({
solutionGrid: grid,
legend: legend, // CrosswordsLegendEntryInput[]
checkOn: "manual",
});| Return | Description |
|---|---|
grid | Current user grid (mutable copy) |
setCell | Update a single cell: setCell(row, col, value) |
wordResults | Per-word correctness after check: { [id]: boolean } where id is {position}-{direction} |
isCorrect | null = not validated, true/false = overall result |
check | Validate current grid against legend solutions |
reset | Clear user input and reset validation state |
resolvedLegend | Full entries with position, direction, startRow, startCol derived from the grid |
getLegendId | Get id for an entry: getLegendId(entry) → "1-horizontal" |