@react-chess-tools/react-chess-puzzle 0.6.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,300 @@
1
+ import type { Meta } from "@storybook/react";
2
+ import React, { useState } from "react";
3
+ import { ChessPuzzle } from "./index";
4
+ import { defaultPuzzleTheme } from "../../theme/defaults";
5
+ import type { ChessPuzzleTheme } from "../../theme/types";
6
+
7
+ const meta = {
8
+ title: "react-chess-puzzle/Theme/Playground",
9
+ component: ChessPuzzle.Root,
10
+ tags: ["theme", "puzzle"],
11
+ decorators: [
12
+ (Story) => (
13
+ <div style={{ maxWidth: "900px" }}>
14
+ <Story />
15
+ </div>
16
+ ),
17
+ ],
18
+ } satisfies Meta<typeof ChessPuzzle.Root>;
19
+
20
+ export default meta;
21
+
22
+ const samplePuzzle = {
23
+ fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
24
+ moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
25
+ makeFirstMove: false,
26
+ };
27
+
28
+ // Color picker component
29
+ const ColorInput: React.FC<{
30
+ label: string;
31
+ value: string;
32
+ onChange: (value: string) => void;
33
+ }> = ({ label, value, onChange }) => {
34
+ const rgbaToHex = (rgba: string): string => {
35
+ const match = rgba.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
36
+ if (match) {
37
+ const r = parseInt(match[1]).toString(16).padStart(2, "0");
38
+ const g = parseInt(match[2]).toString(16).padStart(2, "0");
39
+ const b = parseInt(match[3]).toString(16).padStart(2, "0");
40
+ return `#${r}${g}${b}`;
41
+ }
42
+ return value.startsWith("#") ? value : "#000000";
43
+ };
44
+
45
+ const hexToRgba = (hex: string, alpha: number = 0.5): string => {
46
+ const r = parseInt(hex.slice(1, 3), 16);
47
+ const g = parseInt(hex.slice(3, 5), 16);
48
+ const b = parseInt(hex.slice(5, 7), 16);
49
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
50
+ };
51
+
52
+ return (
53
+ <div
54
+ style={{
55
+ display: "flex",
56
+ alignItems: "center",
57
+ gap: "8px",
58
+ marginBottom: "8px",
59
+ }}
60
+ >
61
+ <input
62
+ type="color"
63
+ value={rgbaToHex(value)}
64
+ onChange={(e) => onChange(hexToRgba(e.target.value))}
65
+ style={{ width: "40px", height: "30px", cursor: "pointer" }}
66
+ />
67
+ <span style={{ fontSize: "12px", minWidth: "100px" }}>{label}</span>
68
+ <input
69
+ type="text"
70
+ value={value}
71
+ onChange={(e) => onChange(e.target.value)}
72
+ style={{ fontSize: "11px", width: "180px", padding: "4px" }}
73
+ />
74
+ </div>
75
+ );
76
+ };
77
+
78
+ export const PuzzlePlayground = () => {
79
+ const [theme, setTheme] = useState<ChessPuzzleTheme>(defaultPuzzleTheme);
80
+ const [copied, setCopied] = useState(false);
81
+ const [puzzleKey, setPuzzleKey] = useState(0);
82
+
83
+ const updatePuzzleColor = (
84
+ key: keyof ChessPuzzleTheme["puzzle"],
85
+ value: string,
86
+ ) => {
87
+ setTheme((prev) => ({
88
+ ...prev,
89
+ puzzle: {
90
+ ...prev.puzzle,
91
+ [key]: value,
92
+ },
93
+ }));
94
+ };
95
+
96
+ const copyTheme = () => {
97
+ const themeCode = `const myPuzzleTheme: PartialChessPuzzleTheme = {
98
+ puzzle: ${JSON.stringify(theme.puzzle, null, 4)}
99
+ };`;
100
+ navigator.clipboard.writeText(themeCode);
101
+ setCopied(true);
102
+ setTimeout(() => setCopied(false), 2000);
103
+ };
104
+
105
+ const resetPuzzle = () => {
106
+ setPuzzleKey((k) => k + 1);
107
+ };
108
+
109
+ return (
110
+ <div style={{ display: "flex", gap: "24px", flexWrap: "wrap" }}>
111
+ <div style={{ flex: "1", minWidth: "300px" }}>
112
+ <h3 style={{ marginBottom: "16px" }}>Puzzle Theme Editor</h3>
113
+
114
+ <div style={{ marginBottom: "16px" }}>
115
+ <strong>Puzzle Colors</strong>
116
+ <div style={{ marginTop: "8px" }}>
117
+ <ColorInput
118
+ label="Success"
119
+ value={theme.puzzle.success}
120
+ onChange={(v) => updatePuzzleColor("success", v)}
121
+ />
122
+ <ColorInput
123
+ label="Failure"
124
+ value={theme.puzzle.failure}
125
+ onChange={(v) => updatePuzzleColor("failure", v)}
126
+ />
127
+ <ColorInput
128
+ label="Hint"
129
+ value={theme.puzzle.hint}
130
+ onChange={(v) => updatePuzzleColor("hint", v)}
131
+ />
132
+ </div>
133
+ </div>
134
+
135
+ <div style={{ display: "flex", gap: "8px", marginBottom: "16px" }}>
136
+ <button
137
+ onClick={copyTheme}
138
+ style={{
139
+ padding: "8px 16px",
140
+ backgroundColor: copied ? "#4caf50" : "#2196f3",
141
+ color: "white",
142
+ border: "none",
143
+ borderRadius: "4px",
144
+ cursor: "pointer",
145
+ }}
146
+ >
147
+ {copied ? "Copied!" : "Copy Theme Code"}
148
+ </button>
149
+ <button
150
+ onClick={resetPuzzle}
151
+ style={{
152
+ padding: "8px 16px",
153
+ backgroundColor: "#666",
154
+ color: "white",
155
+ border: "none",
156
+ borderRadius: "4px",
157
+ cursor: "pointer",
158
+ }}
159
+ >
160
+ Reset Puzzle
161
+ </button>
162
+ </div>
163
+
164
+ <div style={{ fontSize: "12px", color: "#666" }}>
165
+ <p>
166
+ <strong>How to test colors:</strong>
167
+ </p>
168
+ <ul style={{ paddingLeft: "16px" }}>
169
+ <li>Click "Hint" to see the hint color</li>
170
+ <li>Make a correct move to see success color</li>
171
+ <li>Make a wrong move to see failure color</li>
172
+ <li>Click "Reset" to try again</li>
173
+ </ul>
174
+ </div>
175
+ </div>
176
+
177
+ <div style={{ flex: "1", minWidth: "350px" }}>
178
+ <h3 style={{ marginBottom: "16px" }}>Preview</h3>
179
+ <div style={{ maxWidth: "400px" }}>
180
+ <ChessPuzzle.Root key={puzzleKey} puzzle={samplePuzzle} theme={theme}>
181
+ <ChessPuzzle.Board />
182
+ <div style={{ marginTop: "8px", display: "flex", gap: "8px" }}>
183
+ <ChessPuzzle.Hint asChild>
184
+ <button style={{ padding: "6px 12px" }}>Hint</button>
185
+ </ChessPuzzle.Hint>
186
+ <ChessPuzzle.Reset asChild>
187
+ <button style={{ padding: "6px 12px" }}>Reset</button>
188
+ </ChessPuzzle.Reset>
189
+ </div>
190
+ </ChessPuzzle.Root>
191
+ </div>
192
+ <p style={{ fontSize: "12px", color: "#666", marginTop: "8px" }}>
193
+ Solution: Bxd7+, Nxd7, Qb8+, Nxb8, Rd8#
194
+ </p>
195
+ </div>
196
+ </div>
197
+ );
198
+ };
199
+
200
+ export const PuzzleThemeExamples = () => {
201
+ const customThemes = {
202
+ default: defaultPuzzleTheme,
203
+ neon: {
204
+ ...defaultPuzzleTheme,
205
+ puzzle: {
206
+ success: "rgba(0, 255, 127, 0.6)",
207
+ failure: "rgba(255, 0, 127, 0.6)",
208
+ hint: "rgba(0, 191, 255, 0.6)",
209
+ },
210
+ },
211
+ pastel: {
212
+ ...defaultPuzzleTheme,
213
+ puzzle: {
214
+ success: "rgba(152, 251, 152, 0.6)",
215
+ failure: "rgba(255, 182, 193, 0.6)",
216
+ hint: "rgba(173, 216, 230, 0.6)",
217
+ },
218
+ },
219
+ };
220
+
221
+ return (
222
+ <div>
223
+ <h2 style={{ marginBottom: "24px" }}>Puzzle Theme Examples</h2>
224
+ <div
225
+ style={{
226
+ display: "grid",
227
+ gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
228
+ gap: "24px",
229
+ }}
230
+ >
231
+ {Object.entries(customThemes).map(([name, theme]) => (
232
+ <div key={name}>
233
+ <h4 style={{ marginBottom: "8px", textTransform: "capitalize" }}>
234
+ {name}
235
+ </h4>
236
+ <ChessPuzzle.Root puzzle={samplePuzzle} theme={theme}>
237
+ <ChessPuzzle.Board />
238
+ <div style={{ marginTop: "8px", display: "flex", gap: "8px" }}>
239
+ <ChessPuzzle.Hint asChild>
240
+ <button style={{ padding: "4px 8px", fontSize: "12px" }}>
241
+ Hint
242
+ </button>
243
+ </ChessPuzzle.Hint>
244
+ <ChessPuzzle.Reset asChild>
245
+ <button style={{ padding: "4px 8px", fontSize: "12px" }}>
246
+ Reset
247
+ </button>
248
+ </ChessPuzzle.Reset>
249
+ </div>
250
+ </ChessPuzzle.Root>
251
+ </div>
252
+ ))}
253
+ </div>
254
+ </div>
255
+ );
256
+ };
257
+
258
+ export const PartialPuzzleTheme = () => {
259
+ // Only override puzzle colors, inherit game colors from default
260
+ const partialTheme = {
261
+ puzzle: {
262
+ hint: "rgba(255, 215, 0, 0.6)", // Gold
263
+ },
264
+ };
265
+
266
+ return (
267
+ <div style={{ maxWidth: "500px" }}>
268
+ <h3>Partial Puzzle Theme</h3>
269
+ <p style={{ fontSize: "14px", color: "#666", marginBottom: "16px" }}>
270
+ Only override the hint color to gold. Success and failure use defaults.
271
+ </p>
272
+ <ChessPuzzle.Root puzzle={samplePuzzle} theme={partialTheme}>
273
+ <ChessPuzzle.Board />
274
+ <div style={{ marginTop: "8px", display: "flex", gap: "8px" }}>
275
+ <ChessPuzzle.Hint asChild>
276
+ <button style={{ padding: "6px 12px" }}>Show Gold Hint</button>
277
+ </ChessPuzzle.Hint>
278
+ <ChessPuzzle.Reset asChild>
279
+ <button style={{ padding: "6px 12px" }}>Reset</button>
280
+ </ChessPuzzle.Reset>
281
+ </div>
282
+ </ChessPuzzle.Root>
283
+ <details style={{ marginTop: "16px" }}>
284
+ <summary style={{ cursor: "pointer", fontSize: "14px" }}>
285
+ View theme code
286
+ </summary>
287
+ <pre
288
+ style={{
289
+ fontSize: "11px",
290
+ background: "#f5f5f5",
291
+ padding: "12px",
292
+ overflow: "auto",
293
+ }}
294
+ >
295
+ {JSON.stringify(partialTheme, null, 2)}
296
+ </pre>
297
+ </details>
298
+ </div>
299
+ );
300
+ };
@@ -6,15 +6,19 @@ import {
6
6
  } from "@react-chess-tools/react-chess-game";
7
7
  import { getCustomSquareStyles, stringToMove } from "../../../utils";
8
8
  import { useChessPuzzleContext } from "../../..";
9
+ import { useChessPuzzleTheme } from "../../../theme/context";
10
+
11
+ export interface PuzzleBoardProps extends React.ComponentProps<
12
+ typeof ChessGame.Board
13
+ > {}
9
14
 
10
- export interface PuzzleBoardProps
11
- extends React.ComponentProps<typeof ChessGame.Board> {}
12
15
  export const PuzzleBoard: React.FC<PuzzleBoardProps> = ({
13
16
  options = {},
14
17
  ...rest
15
18
  }) => {
16
19
  const puzzleContext = useChessPuzzleContext();
17
20
  const gameContext = useChessGameContext();
21
+ const theme = useChessPuzzleTheme();
18
22
 
19
23
  if (!puzzleContext) {
20
24
  throw new Error("PuzzleContext not found");
@@ -33,6 +37,7 @@ export const PuzzleBoard: React.FC<PuzzleBoardProps> = ({
33
37
  isPlayerTurn,
34
38
  game,
35
39
  stringToMove(game, nextMove),
40
+ theme,
36
41
  ),
37
42
  });
38
43
 
@@ -6,14 +6,26 @@ import {
6
6
  } from "../../../hooks/useChessPuzzle";
7
7
  import { ChessGame } from "@react-chess-tools/react-chess-game";
8
8
  import { ChessPuzzleContext } from "../../../hooks/useChessPuzzleContext";
9
+ import { PuzzleThemeProvider } from "../../../theme/context";
10
+ import { mergePuzzleTheme } from "../../../theme/utils";
11
+ import type { PartialChessPuzzleTheme } from "../../../theme/types";
9
12
 
10
13
  export interface RootProps {
11
14
  puzzle: Puzzle;
12
15
  onSolve?: (puzzleContext: ChessPuzzleContextType) => void;
13
16
  onFail?: (puzzleContext: ChessPuzzleContextType) => void;
17
+ /** Optional theme configuration. Supports partial themes - only override the colors you need. */
18
+ theme?: PartialChessPuzzleTheme;
14
19
  }
15
20
 
16
- const PuzzleRoot: React.FC<React.PropsWithChildren<RootProps>> = ({
21
+ interface PuzzleRootInnerProps {
22
+ puzzle: Puzzle;
23
+ onSolve?: (puzzleContext: ChessPuzzleContextType) => void;
24
+ onFail?: (puzzleContext: ChessPuzzleContextType) => void;
25
+ children: React.ReactNode;
26
+ }
27
+
28
+ const PuzzleRootInner: React.FC<PuzzleRootInnerProps> = ({
17
29
  puzzle,
18
30
  onSolve,
19
31
  onFail,
@@ -32,13 +44,23 @@ export const Root: React.FC<React.PropsWithChildren<RootProps>> = ({
32
44
  puzzle,
33
45
  onSolve,
34
46
  onFail,
47
+ theme,
35
48
  children,
36
49
  }) => {
50
+ // Merge partial theme with defaults
51
+ const mergedTheme = React.useMemo(() => mergePuzzleTheme(theme), [theme]);
52
+
37
53
  return (
38
- <ChessGame.Root fen={puzzle.fen} orientation={getOrientation(puzzle)}>
39
- <PuzzleRoot puzzle={puzzle} onSolve={onSolve} onFail={onFail}>
40
- {children}
41
- </PuzzleRoot>
54
+ <ChessGame.Root
55
+ fen={puzzle.fen}
56
+ orientation={getOrientation(puzzle)}
57
+ theme={mergedTheme}
58
+ >
59
+ <PuzzleThemeProvider theme={mergedTheme}>
60
+ <PuzzleRootInner puzzle={puzzle} onSolve={onSolve} onFail={onFail}>
61
+ {children}
62
+ </PuzzleRootInner>
63
+ </PuzzleThemeProvider>
42
64
  </ChessGame.Root>
43
65
  );
44
66
  };
@@ -0,0 +1,255 @@
1
+ {/* Theming.mdx */}
2
+ import { Meta, ColorPalette, ColorItem } from '@storybook/blocks';
3
+
4
+ <Meta title="react-chess-puzzle/Theming" />
5
+
6
+ # Puzzle Theming
7
+
8
+ React Chess Puzzle extends the game theming system with puzzle-specific colors for hints, success, and failure states.
9
+
10
+ ## Quick Start
11
+
12
+ Pass a theme to `ChessPuzzle.Root` to customize puzzle appearance:
13
+
14
+ ```tsx
15
+ import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
16
+
17
+ const puzzle = {
18
+ fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
19
+ moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
20
+ };
21
+
22
+ function App() {
23
+ return (
24
+ <ChessPuzzle.Root
25
+ puzzle={puzzle}
26
+ theme={{ puzzle: { hint: "rgba(255, 215, 0, 0.6)" } }}
27
+ >
28
+ <ChessPuzzle.Board />
29
+ <ChessPuzzle.Hint>Show Hint</ChessPuzzle.Hint>
30
+ </ChessPuzzle.Root>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ## Puzzle Theme Structure
36
+
37
+ The puzzle theme extends `ChessGameTheme` with an additional `puzzle` section:
38
+
39
+ ```typescript
40
+ interface ChessPuzzleTheme extends ChessGameTheme {
41
+ puzzle: {
42
+ success: string; // Color for correct moves (RGBA)
43
+ failure: string; // Color for incorrect moves (RGBA)
44
+ hint: string; // Color for hint squares (RGBA)
45
+ };
46
+ }
47
+ ```
48
+
49
+ ## Puzzle-Specific Colors
50
+
51
+ The puzzle theme adds three new color options:
52
+
53
+ <table>
54
+ <thead>
55
+ <tr>
56
+ <th>Color</th>
57
+ <th>Usage</th>
58
+ </tr>
59
+ </thead>
60
+ <tbody>
61
+ <tr>
62
+ <td>
63
+ <code>puzzle.success</code>
64
+ </td>
65
+ <td>Highlights squares when a correct move is made</td>
66
+ </tr>
67
+ <tr>
68
+ <td>
69
+ <code>puzzle.failure</code>
70
+ </td>
71
+ <td>Highlights squares when an incorrect move is made</td>
72
+ </tr>
73
+ <tr>
74
+ <td>
75
+ <code>puzzle.hint</code>
76
+ </td>
77
+ <td>Highlights the hint square(s) when hint is requested</td>
78
+ </tr>
79
+ </tbody>
80
+ </table>
81
+
82
+ ## Creating a Custom Puzzle Theme
83
+
84
+ You can customize both game and puzzle colors:
85
+
86
+ ```tsx
87
+ import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
88
+ import type { PartialChessPuzzleTheme } from "@react-chess-tools/react-chess-puzzle";
89
+
90
+ const neonTheme: PartialChessPuzzleTheme = {
91
+ // Customize board colors (inherited from game theme)
92
+ board: {
93
+ lightSquare: { backgroundColor: "#2a2a2a" },
94
+ darkSquare: { backgroundColor: "#1a1a1a" },
95
+ },
96
+ // Customize game state colors
97
+ state: {
98
+ lastMove: "rgba(0, 255, 255, 0.4)",
99
+ },
100
+ // Customize puzzle-specific colors
101
+ puzzle: {
102
+ success: "rgba(0, 255, 127, 0.6)",
103
+ failure: "rgba(255, 0, 127, 0.6)",
104
+ hint: "rgba(0, 191, 255, 0.6)",
105
+ },
106
+ };
107
+
108
+ function App() {
109
+ return (
110
+ <ChessPuzzle.Root puzzle={puzzle} theme={neonTheme}>
111
+ <ChessPuzzle.Board />
112
+ </ChessPuzzle.Root>
113
+ );
114
+ }
115
+ ```
116
+
117
+ ## Partial Theme Overrides
118
+
119
+ Override only the puzzle colors you need:
120
+
121
+ ```tsx
122
+ // Only change the hint color to gold
123
+ const goldHint: PartialChessPuzzleTheme = {
124
+ puzzle: {
125
+ hint: "rgba(255, 215, 0, 0.6)",
126
+ },
127
+ };
128
+
129
+ // Only change success/failure colors
130
+ const customFeedback: PartialChessPuzzleTheme = {
131
+ puzzle: {
132
+ success: "rgba(0, 200, 100, 0.7)",
133
+ failure: "rgba(200, 0, 50, 0.7)",
134
+ },
135
+ };
136
+ ```
137
+
138
+ ## Theme Inheritance
139
+
140
+ Puzzle themes inherit all game theme properties. You can:
141
+
142
+ 1. **Only specify puzzle colors** - game colors use defaults
143
+ 2. **Only specify game colors** - puzzle colors use defaults
144
+ 3. **Mix both** - customize any combination
145
+
146
+ ```tsx
147
+ // Example: Custom board + default puzzle colors
148
+ const customBoard: PartialChessPuzzleTheme = {
149
+ board: {
150
+ lightSquare: { backgroundColor: "#e8e8e8" },
151
+ darkSquare: { backgroundColor: "#4a4a4a" },
152
+ },
153
+ };
154
+
155
+ // Example: Default board + custom puzzle colors
156
+ const customPuzzle: PartialChessPuzzleTheme = {
157
+ puzzle: {
158
+ success: "rgba(100, 255, 100, 0.5)",
159
+ failure: "rgba(255, 100, 100, 0.5)",
160
+ hint: "rgba(100, 100, 255, 0.5)",
161
+ },
162
+ };
163
+ ```
164
+
165
+ ## Using Theme Utilities
166
+
167
+ ### mergePuzzleTheme
168
+
169
+ Merge a partial puzzle theme with defaults:
170
+
171
+ ```tsx
172
+ import {
173
+ mergePuzzleTheme,
174
+ defaultPuzzleTheme,
175
+ } from "@react-chess-tools/react-chess-puzzle";
176
+
177
+ const myTheme = mergePuzzleTheme({
178
+ puzzle: { hint: "rgba(255, 215, 0, 0.6)" },
179
+ });
180
+ // myTheme is now a complete ChessPuzzleTheme
181
+ ```
182
+
183
+ ### useChessPuzzleTheme Hook
184
+
185
+ Access the current puzzle theme from any component:
186
+
187
+ ```tsx
188
+ import { useChessPuzzleTheme } from "@react-chess-tools/react-chess-puzzle";
189
+
190
+ function HintButton() {
191
+ const theme = useChessPuzzleTheme();
192
+
193
+ return (
194
+ <button style={{ backgroundColor: theme.puzzle.hint }}>Show Hint</button>
195
+ );
196
+ }
197
+ ```
198
+
199
+ ## Default Puzzle Colors
200
+
201
+ <ColorPalette>
202
+ <ColorItem
203
+ title="Puzzle Colors"
204
+ subtitle="Puzzle state feedback"
205
+ colors={{
206
+ Success: "rgba(172, 206, 89, 0.5)",
207
+ Failure: "rgba(201, 52, 48, 0.5)",
208
+ Hint: "rgba(27, 172, 166, 0.5)",
209
+ }}
210
+ />
211
+ </ColorPalette>
212
+
213
+ ## Example Theme Variations
214
+
215
+ ### Pastel Theme
216
+
217
+ ```tsx
218
+ const pastelTheme: PartialChessPuzzleTheme = {
219
+ puzzle: {
220
+ success: "rgba(152, 251, 152, 0.6)", // Pale green
221
+ failure: "rgba(255, 182, 193, 0.6)", // Light pink
222
+ hint: "rgba(173, 216, 230, 0.6)", // Light blue
223
+ },
224
+ };
225
+ ```
226
+
227
+ ### High Contrast Theme
228
+
229
+ ```tsx
230
+ const highContrastTheme: PartialChessPuzzleTheme = {
231
+ puzzle: {
232
+ success: "rgba(0, 255, 0, 0.7)",
233
+ failure: "rgba(255, 0, 0, 0.7)",
234
+ hint: "rgba(0, 255, 255, 0.7)",
235
+ },
236
+ };
237
+ ```
238
+
239
+ ## TypeScript Support
240
+
241
+ All puzzle theme types are exported:
242
+
243
+ ```typescript
244
+ import type {
245
+ ChessPuzzleTheme,
246
+ PartialChessPuzzleTheme,
247
+ PuzzleStateTheme,
248
+ } from "@react-chess-tools/react-chess-puzzle";
249
+ ```
250
+
251
+ ## Next Steps
252
+
253
+ - Try the **[Puzzle Theme Playground](/story/react-chess-puzzle-theme-playground--puzzle-playground)** to experiment with colors
254
+ - See **[Puzzle Theme Examples](/story/react-chess-puzzle-theme-playground--puzzle-theme-examples)** for inspiration
255
+ - Check out **[Game Theming](/docs/react-chess-game-theming--docs)** for board and state color options
package/src/index.ts CHANGED
@@ -13,3 +13,17 @@ export type { HintProps } from "./components/ChessPuzzle/parts/Hint";
13
13
  export type { ResetProps } from "./components/ChessPuzzle/parts/Reset";
14
14
  export type { PuzzleBoardProps } from "./components/ChessPuzzle/parts/PuzzleBoard";
15
15
  export type { RootProps } from "./components/ChessPuzzle/parts/Root";
16
+
17
+ // Theme - Types
18
+ export type {
19
+ ChessPuzzleTheme,
20
+ PuzzleStateTheme,
21
+ PartialChessPuzzleTheme,
22
+ } from "./theme/types";
23
+
24
+ // Theme - Values
25
+ export { defaultPuzzleTheme } from "./theme/defaults";
26
+
27
+ // Theme - Utilities
28
+ export { mergePuzzleTheme } from "./theme/utils";
29
+ export { useChessPuzzleTheme, ChessPuzzleThemeContext } from "./theme/context";