From 42d01f8078d9bf58706ff899fa3ab5ebb20b88b4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 06:27:37 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Improve=20GameCard=20?= =?UTF-8?q?accessibility=20and=20fix=20build=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Improved `GameCard.tsx` accessibility by replacing `role="button"` spans with semantic ` ))} {remainingCount > 0 && ( @@ -73,7 +74,8 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, return (
{allMetadata.map((item) => ( - { e.stopPropagation(); @@ -81,11 +83,11 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, console.log('[GameCard] Metadata tag clicked:', item.type, item.name); onFilter?.(item.type, item.name); }} - className="px-1.5 py-0.5 text-xs rounded bg-gray-700/50 theme-text-muted hover:bg-indigo-600/30 transition-colors cursor-pointer select-none" - role="button" + className="px-1.5 py-0.5 text-xs rounded bg-gray-700/50 theme-text-muted hover:bg-indigo-600/30 transition-colors cursor-pointer select-none focus-visible:ring-2 focus-visible:ring-indigo-500/50 outline-none" + aria-label={`${item.type}: ${item.name}`} > {item.name} - + ))}
); @@ -132,18 +134,19 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, const label = COMPLETION_STATUS_LABELS[completion_status as keyof typeof COMPLETION_STATUS_LABELS] || completion_status; return ( - { e.stopPropagation(); e.preventDefault(); console.log('[GameCard] Status badge clicked:', completion_status); onFilter?.('status', completion_status); }} - className={`absolute top-2 left-2 px-2 py-1 rounded-full text-xs text-white ${statusColors[completion_status] || 'bg-gray-600'} hover:opacity-80 transition-opacity cursor-pointer select-none`} - role="button" + className={`absolute top-2 left-2 z-10 px-2 py-1 rounded-full text-xs text-white ${statusColors[completion_status] || 'bg-gray-600'} hover:opacity-80 transition-opacity cursor-pointer select-none focus-visible:ring-2 focus-visible:ring-white/50 outline-none`} + aria-label={`Status: ${label}`} > {label} - + ); }; @@ -166,11 +169,20 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, if (viewMode === "grid") { return (
{ + if (e.key === 'Enter' || e.key === ' ') { + if (e.target !== e.currentTarget) return; + e.preventDefault(); + onClick(game.id); + } + }} onClick={(e) => { // Don't navigate if clicking on an interactive element const target = e.target as HTMLElement; - if (target.closest('[role="button"]') || target.closest('button') || target.closest('a')) { + if (target.closest('button') || target.closest('a')) { console.log('[GameCard] Grid click blocked - interactive element'); return; } @@ -190,13 +202,15 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, {platformIcon && {platformIcon}} {showQuickAssign && ( @@ -235,10 +249,19 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, if (viewMode === "compact") { return (
{ + if (e.key === 'Enter' || e.key === ' ') { + if (e.target !== e.currentTarget) return; + e.preventDefault(); + onClick(game.id); + } + }} onClick={(e) => { const target = e.target as HTMLElement; - if (target.closest('[role="button"]') || target.closest('button') || target.closest('a')) { + if (target.closest('button') || target.closest('a')) { return; } onClick(game.id); @@ -264,6 +287,7 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, {/* Status badge - small */} {completion_status && completion_status !== 'not_started' && ( @@ -339,10 +366,19 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, // List view mode return (
{ + if (e.key === 'Enter' || e.key === ' ') { + if (e.target !== e.currentTarget) return; + e.preventDefault(); + onClick(game.id); + } + }} onClick={(e) => { const target = e.target as HTMLElement; - if (target.closest('[role="button"]') || target.closest('button') || target.closest('a')) { + if (target.closest('button') || target.closest('a')) { return; } onClick(game.id); @@ -374,12 +410,14 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, {platformIcon && {platformIcon}} {showQuickAssign && ( diff --git a/src/components/Library/GameScreenshotsCarousel.tsx b/src/components/Library/GameScreenshotsCarousel.tsx index 6a4e37c..bfae3b8 100644 --- a/src/components/Library/GameScreenshotsCarousel.tsx +++ b/src/components/Library/GameScreenshotsCarousel.tsx @@ -20,7 +20,6 @@ export function GameScreenshotsCarousel({ gameId }: GameScreenshotsCarouselProps const [igdbScreenshots, setIgdbScreenshots] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [loading, setLoading] = useState(true); - const [hasIgdbId, setHasIgdbId] = useState(false); useEffect(() => { const loadScreenshots = async () => { @@ -29,7 +28,6 @@ export function GameScreenshotsCarousel({ gameId }: GameScreenshotsCarouselProps // Load game data to check IGDB ID try { const game = await invoke<{ igdb_id: number | null }>("get_game_by_id", { id: gameId }); - setHasIgdbId(!!game?.igdb_id); // Load IGDB screenshots if available if (game?.igdb_id) {