@@ -170,12 +170,19 @@ export const TrainSection = ({ id }: TrainSectionProps) => {
170170
171171 const [ windowSize , setWindowSize ] = useState ( { width : 0 , height : 0 } )
172172
173+ // Force re-mounts of the board to ensure Chessground recalculates its
174+ // dimensions correctly on small screens after layout shifts.
175+ const [ renderKey , setRenderKey ] = useState ( 0 )
176+
173177 useEffect ( ( ) => {
174178 const handleResize = ( ) => {
175179 setWindowSize ( {
176180 width : window . innerWidth ,
177181 height : window . innerHeight ,
178182 } )
183+
184+ // Re-render the board on window resize so it always fits its container
185+ setRenderKey ( ( prev ) => prev + 1 )
179186 }
180187
181188 handleResize ( )
@@ -195,8 +202,23 @@ export const TrainSection = ({ id }: TrainSectionProps) => {
195202 setCurrentPuzzle ( ( prev ) => ( prev + 1 ) % PUZZLES . length )
196203 } , 8000 )
197204
205+ // Allow the layout to settle and then re-mount the board a few times so
206+ // Chessground can pick up the final container size, similar to the
207+ // behaviour in AnalysisSection.
208+ const timeoutIds : NodeJS . Timeout [ ] = [ ]
209+ const delays = [ 100 , 300 , 500 ]
210+ delays . forEach ( ( delay ) => {
211+ timeoutIds . push (
212+ setTimeout ( ( ) => {
213+ setRenderKey ( ( prev ) => prev + 1 )
214+ } , delay ) ,
215+ )
216+ } )
217+
198218 return ( ) => {
199219 clearInterval ( positionInterval )
220+
221+ timeoutIds . forEach ( ( id ) => clearTimeout ( id ) )
200222 }
201223 } , [ inView ] )
202224
@@ -215,7 +237,7 @@ export const TrainSection = ({ id }: TrainSectionProps) => {
215237
216238 const puzzle = PUZZLES [ currentPuzzle ]
217239
218- const stableKey = `board-${ currentPuzzle } -${ windowSize . width } -${ windowSize . height } `
240+ const stableKey = `board-${ currentPuzzle } -${ renderKey } - ${ windowSize . width } -${ windowSize . height } `
219241
220242 return (
221243 < section
@@ -288,69 +310,71 @@ export const TrainSection = ({ id }: TrainSectionProps) => {
288310 < p className = "text-sm text-secondary" > { puzzle . description } </ p >
289311 </ div >
290312 < div className = "flex flex-col gap-4 p-4 md:flex-row" >
291- < div
292- className = "relative w-full md:w-1/2"
293- style = { {
294- aspectRatio : '1/1' ,
295- transform : 'translateZ(0)' ,
296- } }
297- >
298- < motion . div
299- className = "h-full w-full"
313+ < div className = "flex w-full justify-center md:w-1/2" >
314+ < div
315+ className = "relative w-full"
300316 style = { {
301- position : 'relative ' ,
317+ aspectRatio : '1/1 ' ,
302318 transform : 'translateZ(0)' ,
303319 } }
304320 >
305- < Chessground
306- key = { stableKey }
307- contained
308- config = { {
309- fen : puzzle . fen ,
310- viewOnly : true ,
311- coordinates : true ,
312- drawable : {
313- enabled : true ,
314- visible : true ,
315- defaultSnapToValidMove : true ,
316- shapes,
317- brushes : {
318- green : {
319- key : 'g' ,
320- color : '#15781B' ,
321- opacity : 1 ,
322- lineWidth : 10 ,
323- } ,
324- red : {
325- key : 'r' ,
326- color : '#882020' ,
327- opacity : 1 ,
328- lineWidth : 10 ,
329- } ,
330- blue : {
331- key : 'b' ,
332- color : '#003088' ,
333- opacity : 1 ,
334- lineWidth : 10 ,
335- } ,
336- yellow : {
337- key : 'y' ,
338- color : '#e68f00' ,
339- opacity : 1 ,
340- lineWidth : 10 ,
321+ < motion . div
322+ className = "h-full w-full"
323+ style = { {
324+ position : 'relative' ,
325+ transform : 'translateZ(0)' ,
326+ } }
327+ >
328+ < Chessground
329+ key = { stableKey }
330+ contained
331+ config = { {
332+ fen : puzzle . fen ,
333+ viewOnly : true ,
334+ coordinates : true ,
335+ drawable : {
336+ enabled : true ,
337+ visible : true ,
338+ defaultSnapToValidMove : true ,
339+ shapes,
340+ brushes : {
341+ green : {
342+ key : 'g' ,
343+ color : '#15781B' ,
344+ opacity : 1 ,
345+ lineWidth : 10 ,
346+ } ,
347+ red : {
348+ key : 'r' ,
349+ color : '#882020' ,
350+ opacity : 1 ,
351+ lineWidth : 10 ,
352+ } ,
353+ blue : {
354+ key : 'b' ,
355+ color : '#003088' ,
356+ opacity : 1 ,
357+ lineWidth : 10 ,
358+ } ,
359+ yellow : {
360+ key : 'y' ,
361+ color : '#e68f00' ,
362+ opacity : 1 ,
363+ lineWidth : 10 ,
364+ } ,
341365 } ,
342366 } ,
343- } ,
344- highlight : {
345- lastMove : true ,
346- check : true ,
347- } ,
348- animation : {
349- duration : 0 ,
350- } ,
351- } }
352- /> { ' ' }
353- </ motion . div >
367+ highlight : {
368+ lastMove : true ,
369+ check : true ,
370+ } ,
371+ animation : {
372+ duration : 0 ,
373+ } ,
374+ } }
375+ /> { ' ' }
376+ </ motion . div >
377+ </ div >
354378 </ div >
355379 < div className = "flex w-full flex-col md:w-1/2" >
356380 < motion . div
0 commit comments