Skip to content

feat(seo): category pages, genre taxonomy and IGDB critic scores - #51

Merged
neo773 merged 2 commits into
masterfrom
feat/seo-category-pages-genres-critic-scores
Sep 20, 2026
Merged

neo773 merged 2 commits into
masterfrom
feat/seo-category-pages-genres-critic-scores

Conversation

@neo773

@neo773 neo773 commented Sep 20, 2026

Copy link
Copy Markdown
Owner

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-games hub, chip pages (M1–M5), play-method pages, and 10 genre pages. Each has a unique sub-60-char title, its own description, one h1, a canonical, and server-rendered CollectionPage + BreadcrumbList JSON-LD. getGames and getFilterCounts take 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 requests aggregated_rating + aggregated_rating_count and 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):

  • 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 (three came from Steam's own HTML — 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

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.

Deploy notes

  • Migration 0010 adds two nullable columns. The API container already runs migrate-database on boot, so this applies itself on deploy.
  • After deploy, run backfill-game-genres (with --dry-run first) — without it, production genres stay unnormalized and the genre pages return incomplete results.
  • backfill-critic-ratings needs TWITCH_CLIENT_ID / TWITCH_CLIENT_SECRET and 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: 1 h1 per page, zero console errors, filter counts verified against the database at production scale.

🤖 Generated with Claude Code

neo773 and others added 2 commits September 20, 2026 22:48
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>
@neo773
neo773 merged commit 66106bc into master Sep 20, 2026
2 checks passed
@neo773
neo773 deleted the feat/seo-category-pages-genres-critic-scores branch September 20, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant