Repository navigation
feat(seo): category pages, genre taxonomy and IGDB critic scores - #51
Merged
Merged
Conversation
Addresses most of the issue register in docs/seo-issues.md, built from the 16-month Search Console export and an Ahrefs backlink scrape. Category pages (19 new routes) - /mac-games hub plus chip (M1-M5), play-method and genre landing pages - each has a unique sub-60-char title, its own description, one h1, a canonical and server-rendered CollectionPage + BreadcrumbList JSON-LD - getGames/getFilterCounts accept a genre filter; hub-and-spoke internal links between every category Genre taxonomy - 43 raw labels across five languages collapsed onto 14 canonical genres - normalizeGenres applied at write time in both the IGDB and Steam drivers - backfill-game-genres command rewrites existing rows (287 locally) - genre filter added to the home page, validated by a type guard IGDB critic scores - request aggregated_rating/aggregated_rating_count instead of the unused total_rating; store both on Game - extractIgdbCriticRating returns null when no critic has scored the game, so a rating is never shown without reports behind it - backfill-critic-ratings command, batched via external_games Game page rewrite (all 1,572 pages) - Mac verdict paragraph now leads the page, gated by report count: 3+ reports with fps gives a verdict, 1-2 says so plainly, 0 says nothing - titles 80 -> 35-48 chars, descriptions 183 -> ~125 - seven h1 tags reduced to one; storefront headings demoted on render - JSON-LD moved from next/script to a server-rendered tag so non-JS crawlers can read it - Steam description wrapped in data-nosnippet so the verdict is what Google quotes - release-year and genre badges, genre linking to its category page Also: sitemap now includes the home page, blog, contributors and all category routes; /?q= search URLs are noindex; home, blog and contributors have unique titles and canonicals. Requires migration 0010 to be applied before deploy, and backfill-game-genres to be run after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Built from the 16-month Search Console export and an Ahrefs backlink scrape. Full issue register in
docs/seo-issues.md.What this does
19 new category pages —
/mac-gameshub, chip pages (M1–M5), play-method pages, and 10 genre pages. Each has a unique sub-60-char title, its own description, oneh1, a canonical, and server-renderedCollectionPage+BreadcrumbListJSON-LD.getGamesandgetFilterCountstake a genre filter.Genre taxonomy — 43 raw labels across five languages (
Azione,인디,Simulationen,Massively Multiplayer…) collapsed onto 14 canonical genres. Normalized at write time in both the IGDB and Steam drivers, plus a backfill for existing rows. Genre filter added to the home page.IGDB critic scores — we were requesting
total_rating(critics and users mixed) and discarding it. Now requestsaggregated_rating+aggregated_rating_countand stores both. Returns null when no critic has scored the game, so a rating is never shown without reports behind it.Game page rewrite (all 1,572 pages):
h1tags reduced to one (three came from Steam's own HTML — demoted on render)next/scriptto a server-rendered tag so non-JS crawlers can read itdata-nosnippetso the verdict is what Google quotesSitemap now includes the home page, blog, contributors and all category routes.
/?q=search URLs arenoindex. Home, blog and contributors have unique titles and canonicals.Deploy notes
0010adds two nullable columns. The API container already runsmigrate-databaseon boot, so this applies itself on deploy.backfill-game-genres(with--dry-runfirst) — without it, production genres stay unnormalized and the genre pages return incomplete results.backfill-critic-ratingsneedsTWITCH_CLIENT_ID/TWITCH_CLIENT_SECRETand has never been run.Checks
bunx tsc --noEmit(root + server),bun run lint,bun run test— 106/106 passing (was 67). Driven in headless Chromium: 1h1per page, zero console errors, filter counts verified against the database at production scale.🤖 Generated with Claude Code