accessibility

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-11-11 23:39:16 +01:00
padre dd7c7b97e2
commit 7c4087a34c
Se han modificado 6 ficheros con 70 adiciones y 22 borrados

Ver fichero

@@ -131,7 +131,7 @@ export default function Home() {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-100 to-slate-200"> <div className="min-h-screen bg-gradient-to-br from-slate-100 to-slate-200">
{/* Header */} {/* Header */}
<header className="bg-white shadow-md"> <header className="bg-white shadow-md" role="banner">
<div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between"> <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"> <h1 className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
@@ -148,12 +148,15 @@ export default function Home() {
<button <button
onClick={() => setShowRules(!showRules)} onClick={() => setShowRules(!showRules)}
className="text-gray-600 hover:text-gray-900 text-sm font-medium" className="text-gray-600 hover:text-gray-900 text-sm font-medium"
aria-label="Toggle game rules"
aria-expanded={showRules}
> >
Rules Rules
</button> </button>
<button <button
onClick={leaveRoom} onClick={leaveRoom}
className="bg-red-500 text-white px-4 py-2 rounded-lg text-sm font-semibold hover:bg-red-600 transition-colors" className="bg-red-500 text-white px-4 py-2 rounded-lg text-sm font-semibold hover:bg-red-600 transition-colors"
aria-label="Leave current game"
> >
Leave Game Leave Game
</button> </button>
@@ -169,6 +172,9 @@ export default function Home() {
animate={{ y: 0, opacity: 1 }} animate={{ y: 0, opacity: 1 }}
exit={{ y: -100, opacity: 0 }} exit={{ y: -100, opacity: 0 }}
className="fixed top-20 left-1/2 transform -translate-x-1/2 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg z-50" className="fixed top-20 left-1/2 transform -translate-x-1/2 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg z-50"
role="alert"
aria-live="assertive"
aria-atomic="true"
> >
{error} {error}
</motion.div> </motion.div>
@@ -184,6 +190,9 @@ export default function Home() {
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50" className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"
onClick={() => setShowRules(false)} onClick={() => setShowRules(false)}
role="dialog"
aria-modal="true"
aria-labelledby="rules-title"
> >
<motion.div <motion.div
initial={{ scale: 0.9 }} initial={{ scale: 0.9 }}
@@ -192,7 +201,7 @@ export default function Home() {
className="bg-white rounded-lg p-6 max-w-md" className="bg-white rounded-lg p-6 max-w-md"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<h3 className="text-xl font-bold mb-4">How to Play</h3> <h3 id="rules-title" className="text-xl font-bold mb-4">How to Play</h3>
<ul className="space-y-2 text-sm text-gray-700"> <ul className="space-y-2 text-sm text-gray-700">
<li> Click on a tile to select it</li> <li> Click on a tile to select it</li>
<li> Click &quot;Place Left&quot; or &quot;Place Right&quot; to place it</li> <li> Click &quot;Place Left&quot; or &quot;Place Right&quot; to place it</li>
@@ -204,6 +213,7 @@ export default function Home() {
<button <button
onClick={() => setShowRules(false)} onClick={() => setShowRules(false)}
className="mt-4 w-full bg-blue-500 text-white py-2 rounded-lg" className="mt-4 w-full bg-blue-500 text-white py-2 rounded-lg"
aria-label="Close rules dialog"
> >
Close Close
</button> </button>
@@ -213,12 +223,12 @@ export default function Home() {
</AnimatePresence> </AnimatePresence>
{/* Main game area */} {/* Main game area */}
<div className="max-w-7xl mx-auto px-4 py-6"> <main className="max-w-7xl mx-auto px-4 py-6" role="main">
<div className="grid lg:grid-cols-[1fr_300px] gap-6"> <div className="grid lg:grid-cols-[1fr_300px] gap-6">
{/* Left side - Game board and controls */} {/* Left side - Game board and controls */}
<div className="space-y-4"> <div className="space-y-4">
{/* Game info */} {/* Game info */}
<div className="bg-white rounded-lg shadow-md p-4"> <div className="bg-white rounded-lg shadow-md p-4" role="status" aria-live="polite" aria-atomic="true">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<div className="text-sm text-gray-600">Current Turn</div> <div className="text-sm text-gray-600">Current Turn</div>
@@ -255,11 +265,12 @@ export default function Home() {
)} )}
</div> </div>
)} )}
<div className="flex items-center gap-3 flex-wrap"> <div className="flex items-center gap-3 flex-wrap" role="group" aria-label="Game controls">
<button <button
onClick={() => handlePlaceTile('left')} onClick={() => handlePlaceTile('left')}
disabled={!selectedTile} disabled={!selectedTile}
className="flex-1 bg-gradient-to-r from-blue-500 to-blue-600 text-white py-3 rounded-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-lg transition-shadow" className="flex-1 bg-gradient-to-r from-blue-500 to-blue-600 text-white py-3 rounded-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-lg transition-shadow"
aria-label={selectedTile ? `Place tile ${selectedTile.left}-${selectedTile.right} on the left side` : 'Place tile on left side (select a tile first)'}
> >
Place Left Place Left
</button> </button>
@@ -267,6 +278,7 @@ export default function Home() {
onClick={() => handlePlaceTile('right')} onClick={() => handlePlaceTile('right')}
disabled={!selectedTile} disabled={!selectedTile}
className="flex-1 bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 rounded-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-lg transition-shadow" className="flex-1 bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 rounded-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-lg transition-shadow"
aria-label={selectedTile ? `Place tile ${selectedTile.left}-${selectedTile.right} on the right side` : 'Place tile on right side (select a tile first)'}
> >
Place Right Place Right
</button> </button>
@@ -274,6 +286,7 @@ export default function Home() {
<button <button
onClick={drawTile} onClick={drawTile}
className="flex-1 bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-shadow" className="flex-1 bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-shadow"
aria-label={`Draw a tile from the boneyard (${gameState.boneyard.length} tiles remaining)`}
> >
Draw Tile Draw Tile
</button> </button>
@@ -282,6 +295,7 @@ export default function Home() {
<button <button
onClick={handlePass} onClick={handlePass}
className="flex-1 bg-gradient-to-r from-yellow-500 to-yellow-600 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-shadow" className="flex-1 bg-gradient-to-r from-yellow-500 to-yellow-600 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-shadow"
aria-label="Pass your turn (no valid moves available)"
> >
Pass Turn Pass Turn
</button> </button>
@@ -303,7 +317,7 @@ export default function Home() {
</div> </div>
{/* Right side - Other players */} {/* Right side - Other players */}
<div className="space-y-4"> <aside className="space-y-4" role="complementary" aria-label="Other players">
<h3 className="text-lg font-semibold text-gray-700">Players</h3> <h3 className="text-lg font-semibold text-gray-700">Players</h3>
{gameState.players {gameState.players
.filter(p => p.id !== currentPlayerId) .filter(p => p.id !== currentPlayerId)
@@ -338,10 +352,10 @@ export default function Home() {
))} ))}
</div> </div>
</div> </div>
))} )}
</div> </aside>
</div> </div>
</div> </main>
{/* Game over modal */} {/* Game over modal */}
{gameState.isGameOver && ( {gameState.isGameOver && (

Ver fichero

@@ -163,7 +163,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
}; };
return ( return (
<div className={className}> <div className={className} role="region" aria-label="Game board">
<canvas <canvas
ref={canvasRef} ref={canvasRef}
width={width} width={width}
@@ -173,6 +173,8 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
onMouseMove={handleMouseMove} onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp} onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp} onMouseLeave={handleMouseUp}
role="img"
aria-label={`Domino board with ${placedTiles.length} tiles placed`}
/> />
{placedTiles.length > 0 && ( {placedTiles.length > 0 && (
<div className="mt-2 text-center text-sm text-gray-600"> <div className="mt-2 text-center text-sm text-gray-600">

Ver fichero

@@ -17,6 +17,9 @@ export function GameOver({ winner, players, onPlayAgain, onLeave }: GameOverProp
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50" className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"
role="dialog"
aria-modal="true"
aria-labelledby="game-over-title"
> >
<motion.div <motion.div
initial={{ scale: 0.8, y: 50 }} initial={{ scale: 0.8, y: 50 }}
@@ -32,7 +35,7 @@ export function GameOver({ winner, players, onPlayAgain, onLeave }: GameOverProp
> >
{winner ? '🏆' : '🤝'} {winner ? '🏆' : '🤝'}
</motion.div> </motion.div>
<h2 className="text-3xl font-bold text-gray-800 mb-2"> <h2 id="game-over-title" className="text-3xl font-bold text-gray-800 mb-2">
{winner ? 'Game Over!' : 'Game Blocked!'} {winner ? 'Game Over!' : 'Game Blocked!'}
</h2> </h2>
{winner && ( {winner && (
@@ -91,6 +94,7 @@ export function GameOver({ winner, players, onPlayAgain, onLeave }: GameOverProp
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={onPlayAgain} onClick={onPlayAgain}
className="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow" className="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow"
aria-label="Start a new game"
> >
Play Again Play Again
</motion.button> </motion.button>
@@ -100,8 +104,9 @@ export function GameOver({ winner, players, onPlayAgain, onLeave }: GameOverProp
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={onLeave} onClick={onLeave}
className="w-full bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold hover:bg-gray-400 transition-colors" className="w-full bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold hover:bg-gray-400 transition-colors"
aria-label="Leave game and return to main menu"
> >
Back to Menu Leave Game
</motion.button> </motion.button>
</div> </div>
</motion.div> </motion.div>

Ver fichero

@@ -32,7 +32,7 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
if (mode === 'menu') { if (mode === 'menu') {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center p-4"> <main className="min-h-screen bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center p-4" role="main">
<motion.div <motion.div
initial={{ scale: 0.9, opacity: 0 }} initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }} animate={{ scale: 1, opacity: 1 }}
@@ -48,23 +48,28 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
<p className="text-center text-gray-600 mb-8">Online Multiplayer Game</p> <p className="text-center text-gray-600 mb-8">Online Multiplayer Game</p>
<div className="space-y-4 mb-6"> <div className="space-y-4 mb-6">
<label htmlFor="player-name" className="sr-only">Your name</label>
<input <input
id="player-name"
type="text" type="text"
placeholder="Enter your name" placeholder="Enter your name"
value={playerName} value={playerName}
onChange={(e) => setPlayerName(e.target.value)} onChange={(e) => setPlayerName(e.target.value)}
className="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none transition-colors" className="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none transition-colors"
maxLength={20} maxLength={20}
aria-label="Enter your player name"
aria-required="true"
/> />
</div> </div>
<div className="space-y-3"> <div className="space-y-3" role="group" aria-label="Game mode selection">
<motion.button <motion.button
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={handleCreate} onClick={handleCreate}
disabled={!playerName.trim()} disabled={!playerName.trim()}
className="w-full bg-gradient-to-r from-blue-500 to-blue-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" className="w-full bg-gradient-to-r from-blue-500 to-blue-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed"
aria-label="Create a new multiplayer room"
> >
Create Room Create Room
</motion.button> </motion.button>
@@ -75,6 +80,7 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
onClick={() => setMode('join')} onClick={() => setMode('join')}
disabled={!playerName.trim()} disabled={!playerName.trim()}
className="w-full bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" className="w-full bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed"
aria-label="Join an existing multiplayer room"
> >
Join Room Join Room
</motion.button> </motion.button>
@@ -85,6 +91,7 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
onClick={handleAI} onClick={handleAI}
disabled={!playerName.trim()} disabled={!playerName.trim()}
className="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" className="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed"
aria-label="Start a game against computer AI"
> >
Play vs AI Play vs AI
</motion.button> </motion.button>
@@ -94,13 +101,13 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
<p>Built with Next.js, Canvas & Socket.IO</p> <p>Built with Next.js, Canvas & Socket.IO</p>
</div> </div>
</motion.div> </motion.div>
</div> </main>
); );
} }
if (mode === 'join') { if (mode === 'join') {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center p-4"> <main className="min-h-screen bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center p-4" role="main">
<motion.div <motion.div
initial={{ scale: 0.9, opacity: 0 }} initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }} animate={{ scale: 1, opacity: 1 }}
@@ -109,13 +116,17 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
<h2 className="text-3xl font-bold text-center mb-6 text-gray-800">Join Room</h2> <h2 className="text-3xl font-bold text-center mb-6 text-gray-800">Join Room</h2>
<div className="space-y-4 mb-6"> <div className="space-y-4 mb-6">
<label htmlFor="room-id" className="sr-only">Room ID</label>
<input <input
id="room-id"
type="text" type="text"
placeholder="Enter Room ID" placeholder="Enter Room ID"
value={joinRoomId} value={joinRoomId}
onChange={(e) => setJoinRoomId(e.target.value.toUpperCase())} onChange={(e) => setJoinRoomId(e.target.value.toUpperCase())}
className="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:border-purple-500 focus:outline-none transition-colors uppercase" className="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:border-purple-500 focus:outline-none transition-colors uppercase"
maxLength={6} maxLength={6}
aria-label="Enter the 6-character room ID"
aria-required="true"
/> />
</div> </div>
@@ -126,6 +137,7 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
onClick={handleJoin} onClick={handleJoin}
disabled={!joinRoomId.trim()} disabled={!joinRoomId.trim()}
className="w-full bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" className="w-full bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow disabled:opacity-50 disabled:cursor-not-allowed"
aria-label="Join the room with entered ID"
> >
Join Game Join Game
</motion.button> </motion.button>
@@ -135,12 +147,13 @@ export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProp
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={() => setMode('menu')} onClick={() => setMode('menu')}
className="w-full bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold hover:bg-gray-400 transition-colors" className="w-full bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold hover:bg-gray-400 transition-colors"
aria-label="Go back to main menu"
> >
Back Back
</motion.button> </motion.button>
</div> </div>
</motion.div> </motion.div>
</div> </main>
); );
} }

Ver fichero

@@ -20,7 +20,7 @@ export function PlayerHand({
validTileIds, validTileIds,
}: PlayerHandProps) { }: PlayerHandProps) {
return ( return (
<div className="bg-white rounded-lg shadow-lg p-4"> <div className="bg-white rounded-lg shadow-lg p-4" role="region" aria-label="Your tiles">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-full flex items-center justify-center font-bold text-white ${ <div className={`w-10 h-10 rounded-full flex items-center justify-center font-bold text-white ${
@@ -63,6 +63,16 @@ export function PlayerHand({
className={`relative ${ className={`relative ${
isPlayable && isCurrentPlayer ? 'cursor-pointer' : 'cursor-not-allowed' isPlayable && isCurrentPlayer ? 'cursor-pointer' : 'cursor-not-allowed'
}`} }`}
role="button"
tabIndex={isPlayable && isCurrentPlayer ? 0 : -1}
aria-label={`Tile ${tile.left}-${tile.right}${isSelected ? ' (selected)' : ''}${!isPlayable ? ' (cannot be played)' : ''}`}
aria-pressed={isSelected}
onKeyDown={(e) => {
if ((e.key === 'Enter' || e.key === ' ') && isPlayable && isCurrentPlayer) {
e.preventDefault();
onTileClick(tile.id);
}
}}
> >
<DominoTileSVG <DominoTileSVG
tile={tile} tile={tile}
@@ -96,6 +106,8 @@ function DominoTileSVG({ tile, isSelected, isPlayable }: DominoTileSVGProps) {
className={`transition-all ${ className={`transition-all ${
isSelected ? 'ring-4 ring-blue-500 rounded' : '' isSelected ? 'ring-4 ring-blue-500 rounded' : ''
} ${!isPlayable ? 'opacity-50' : ''}`} } ${!isPlayable ? 'opacity-50' : ''}`}
role="img"
aria-label={`Domino tile: ${tile.left} and ${tile.right}`}
> >
{/* Background */} {/* Background */}
<rect <rect

Ver fichero

@@ -18,7 +18,7 @@ export function WaitingRoom({ roomId, players, currentPlayerId, onReady, onLeave
const canStart = players.length >= 2 && players.every(p => p.isReady); const canStart = players.length >= 2 && players.every(p => p.isReady);
return ( return (
<div className="min-h-screen bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center p-4"> <main className="min-h-screen bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center p-4" role="main">
<motion.div <motion.div
initial={{ scale: 0.9, opacity: 0 }} initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }} animate={{ scale: 1, opacity: 1 }}
@@ -102,11 +102,12 @@ export function WaitingRoom({ roomId, players, currentPlayerId, onReady, onLeave
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={onReady} onClick={onReady}
className="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow" className="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-lg font-semibold shadow-lg hover:shadow-xl transition-shadow"
aria-label="Mark yourself as ready to start the game"
> >
Ready to Play Ready to Play
</motion.button> </motion.button>
) : ( ) : (
<div className="w-full bg-green-100 border-2 border-green-500 text-green-700 py-3 rounded-lg font-semibold text-center"> <div className="w-full bg-green-100 border-2 border-green-500 text-green-700 py-3 rounded-lg font-semibold text-center" role="status" aria-live="polite">
{canStart ? 'Starting game...' : players.length < 2 ? 'Waiting for at least 1 more player...' : 'Waiting for other players...'} {canStart ? 'Starting game...' : players.length < 2 ? 'Waiting for at least 1 more player...' : 'Waiting for other players...'}
</div> </div>
)} )}
@@ -116,12 +117,13 @@ export function WaitingRoom({ roomId, players, currentPlayerId, onReady, onLeave
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={onLeave} onClick={onLeave}
className="w-full bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold hover:bg-gray-400 transition-colors" className="w-full bg-gray-300 text-gray-700 py-3 rounded-lg font-semibold hover:bg-gray-400 transition-colors"
aria-label="Leave the waiting room and return to menu"
> >
Leave Room Leave Room
</motion.button> </motion.button>
</div> </div>
<div className="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg"> <div className="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg" role="region" aria-label="Game rules">
<h4 className="font-semibold text-blue-900 mb-2">Game Rules</h4> <h4 className="font-semibold text-blue-900 mb-2">Game Rules</h4>
<ul className="text-sm text-blue-800 space-y-1"> <ul className="text-sm text-blue-800 space-y-1">
<li> 2-4 players can play (minimum 2 required)</li> <li> 2-4 players can play (minimum 2 required)</li>
@@ -132,6 +134,6 @@ export function WaitingRoom({ roomId, players, currentPlayerId, onReady, onLeave
</ul> </ul>
</div> </div>
</motion.div> </motion.div>
</div> </main>
); );
} }