diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..ea026fd --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2025-05-14 - [Semantic Buttons and Card Accessibility] +**Learning:** Using semantic ` ))} {remainingCount > 0 && ( @@ -73,19 +73,19 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, return (
{allMetadata.map((item) => ( - { e.stopPropagation(); e.preventDefault(); 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-offset-1 focus-visible:ring-indigo-500 outline-none" > {item.name} - + ))}
); @@ -132,18 +132,18 @@ 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 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-offset-1 focus-visible:ring-indigo-500 outline-none`} > {label} - + ); }; @@ -166,7 +166,10 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, if (viewMode === "grid") { return (
{ // Don't navigate if clicking on an interactive element const target = e.target as HTMLElement; @@ -177,6 +180,13 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, console.log('[GameCard] Grid click - navigating to game:', game.id); onClick(game.id); }} + onKeyDown={(e) => { + if (e.target !== e.currentTarget) return; + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(game.id); + } + }} >
{renderCover()} @@ -235,7 +245,10 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, if (viewMode === "compact") { return (
{ const target = e.target as HTMLElement; if (target.closest('[role="button"]') || target.closest('button') || target.closest('a')) { @@ -243,6 +256,13 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, } onClick(game.id); }} + onKeyDown={(e) => { + if (e.target !== e.currentTarget) return; + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(game.id); + } + }} >
{cover_url ? ( @@ -339,7 +359,10 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, // List view mode return (
{ const target = e.target as HTMLElement; if (target.closest('[role="button"]') || target.closest('button') || target.closest('a')) { @@ -347,6 +370,13 @@ const GameCard: React.FC = ({ game, onClick, viewMode, onFilter, } onClick(game.id); }} + onKeyDown={(e) => { + if (e.target !== e.currentTarget) return; + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(game.id); + } + }} >
{cover_url ? ( 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) {