From bded41f52261a8bf3e4eb3057b707497cb4379f6 Mon Sep 17 00:00:00 2001 From: LeonaS Date: Thu, 8 Oct 2026 13:32:32 +0200 Subject: [PATCH 1/3] feat(frontend): legg til GameSummary-type for spillkort --- frontend/src/types/game.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/src/types/game.ts diff --git a/frontend/src/types/game.ts b/frontend/src/types/game.ts new file mode 100644 index 00000000..0829d119 --- /dev/null +++ b/frontend/src/types/game.ts @@ -0,0 +1,13 @@ +//Inneholder dataene ett spillekort trenger. Feltene speiler tabellen 'games' + sjangernavn slik de vil komme fra GraphQL-APIet. + +export interface GameSummary { + id: string; + name: string; + released: string | null; + backgroundImage: string | null; + // RAWG-rating, 0-5 + rating: number | null; + // Metacritic score, 0-100 + metacritic: number | null; + genres: string[]; +} From ac70bf58c55a8ae261a7e87ea4e6a284a4db8011 Mon Sep 17 00:00:00 2001 From: LeonaS Date: Thu, 8 Oct 2026 13:32:43 +0200 Subject: [PATCH 2/3] feat(frontend): legg til GameCard-komponent --- frontend/src/components/GameCard/GameCard.tsx | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 frontend/src/components/GameCard/GameCard.tsx diff --git a/frontend/src/components/GameCard/GameCard.tsx b/frontend/src/components/GameCard/GameCard.tsx new file mode 100644 index 00000000..b2deeb3b --- /dev/null +++ b/frontend/src/components/GameCard/GameCard.tsx @@ -0,0 +1,64 @@ +import type { GameSummary } from '../../types/game'; +import './GameCard.css'; + +interface GameCardProps { + game: GameSummary; + isFavorite: boolean; + onToggleFavorite: (id: string) => void; +} + +//Viser ett spill i resultatlista. Lenken i tittelen dekker hele kortet (via CSS), så hele kortet er klikkbart, men skjermlesere hører bare én lenke per kort. + +export function GameCard({ game, isFavorite, onToggleFavorite }: GameCardProps) { + const year = game.released?.slice(0, 4) ?? null; + const meta = [year, game.genres.slice(0, 2).join(', ')].filter(Boolean).join(' · '); + + return ( +
+ {game.backgroundImage ? ( + // alt="" fordi tittelen allerede beskriver kortet. Fast størrelse og lazy + // loading hindrer at layouten hopper og sparer data ved lange lister. + + ) : ( +
+ ); +} From 885d705f2f1e86cfa6cd38fe1bcf4d5159e1efd0 Mon Sep 17 00:00:00 2001 From: LeonaS Date: Thu, 8 Oct 2026 13:33:03 +0200 Subject: [PATCH 3/3] feat(frontend): stil for GameCard --- frontend/src/components/GameCard/GameCard.css | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 frontend/src/components/GameCard/GameCard.css diff --git a/frontend/src/components/GameCard/GameCard.css b/frontend/src/components/GameCard/GameCard.css new file mode 100644 index 00000000..8566b789 --- /dev/null +++ b/frontend/src/components/GameCard/GameCard.css @@ -0,0 +1,118 @@ +/* Én rad i resultatlista. Ingen boks, bare en skillelinje under. */ +.game-card { + position: relative; + display: grid; + grid-template-columns: 192px 1fr; + gap: 20px; + padding: 20px 0; + text-align: left; + border-bottom: 1px solid var(--border); +} + +.game-card__image { + display: block; + width: 100%; + height: auto; + aspect-ratio: 16 / 9; + object-fit: cover; + background: var(--code-bg); /* vises mens bildet laster / når det mangler */ +} + +.game-card__body { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; +} + +.game-card__title { + margin: 0; + font-size: 20px; + font-weight: 600; + line-height: 1.25; +} + +.game-card__title a { + color: var(--text-h); + text-decoration: none; +} + +/* Strekker lenken over hele raden, så hele raden er klikkbar */ +.game-card__title a::after { + content: ''; + position: absolute; + inset: 0; +} + +.game-card:hover .game-card__title a { + text-decoration: underline; + text-underline-offset: 3px; +} + +/* Kantete fokusring rundt hele raden ved tastaturnavigasjon */ +.game-card__title a:focus-visible { + outline: none; +} +.game-card:has(.game-card__title a:focus-visible) { + outline: 2px solid var(--text-h); + outline-offset: 4px; +} + +.game-card__meta { + font-size: 15px; +} + +.game-card__scores { + display: flex; + gap: 24px; + margin: 4px 0 8px; + font-size: 15px; +} + +.game-card__scores div { + display: flex; + gap: 6px; +} + +.game-card__scores dd { + margin: 0; + color: var(--text-h); + font-variant-numeric: tabular-nums; /* tallene står rett under hverandre i lista */ +} + +/* Tekstknapp med rett ramme, som [+ Favoritt] i skissen */ +.game-card__favorite { + position: relative; + z-index: 1; /* over den strukne lenken */ + margin-top: auto; + min-height: 44px; /* stort nok trykkmål */ + padding: 0 14px; + font: inherit; + font-size: 15px; + color: var(--text-h); + background: none; + border: 1px solid var(--text-h); + border-radius: 0; + cursor: pointer; +} + +.game-card__favorite:hover { + text-decoration: underline; +} + +.game-card__favorite[aria-pressed='true'] { + color: var(--bg); + background: var(--text-h); +} + +.game-card__favorite:focus-visible { + outline: 2px solid var(--text-h); + outline-offset: 2px; +} + +@media (max-width: 480px) { + .game-card { + grid-template-columns: 1fr; + gap: 12px; + } +}