feat(seo): give the home, playstyle and reveals pages their own link preview images (#1135)
Merged
DO
4d ago+974+974+012345678901234567890123456789-104-104-012345678901234567890123456789
Closes #1135
What changes
A link to the home page, a playstyle page or, in reveal season, /reveals now previews with an image of its own, and every other page previews with the new home image instead of the old wordmark banner (/og-image.png). Each image is 1200x630:
- Home: the six featured cards in an arc, each glowing in its ink, under "Find the cards that work together".
- Playstyle (25): the gallery tile's fan (the hero, then the next four cards with art by name, Locations left out), "Lorcana playstyle · N cards", the name, the tagline and three key cards, in the playstyle's accent.
- Reveals (in season only): the set's name, "N of 204 cards revealed" with a progress bar, and the five newest revealed cards.
Details:
scripts/generate-og-images.mjsdraws the page images on a fifth page beside the four card pages, withscripts/og/page-template.html. It writesdist/og/page/{home|reveals|playstyle-<id>}.{sha256 prefix}.jpgand a second record,apps/web/node_modules/.cache/og-pages.json. Locally: 26 page images (27 in a faked season), 2.0 MiB, with no change to the run's 30 s.scripts/lib/pageOgImage.mjsholds the pure parts: what each image shows, its alt text, which image a route names (pageOgKey,ogImageForRoute), the season test, and the parsers forPLAYSTYLE_UIandPER_INK. Its tests are inscripts/lib/__tests__/pageOgImage.test.mjs, and they parse the app's realplaystyleUi.tsandrevealSet.ts.scripts/prerender.mjswrites each page's own image, or the home image, into every page it captures.isRevealSeasonActivenow calls the helper'srevealSeasonSet, so the crawl and the generator always agree on the season.- Check 13 in
scripts/check-rendered-html.mjsfails the deploy when/, a playstyle page or/reveals(when the crawl wrote it) doesn't name its own/og/page/image, or another prerendered route doesn't name the home image. It also fails whentwitter:imagediffers fromog:image, or when the build lacks the file. Check 12's image test is now shared with it. withCardOgImageis renamedwithOgImage, since every page uses it now.deploy.yml's dry-run paths add the new helper.build:vercel's fallback message names both checks.- Docs:
CLAUDE.md's Link previews note andIMAGES.md.
Where it differs from the issue
- The playstyle image's "Key cards" names the fan's first three distinct names, so a playstyle whose fan holds two Aladdins shows two names, not "Aladdin · Aladdin".
homeOgDatatakes the featured ids and the card list. The issue had it readfeaturedCards.json's cards directly, but that file's cards carry no image hash.
Verification
-
pnpm test:scripts: 1195 passed (10 new helper tests, 5 for check 13) -
lint:root, knip, and the CodeScene gate (Code Health 10 on every changed script) - Three generator runs and a rebuild give the same 26 file names
- Every page JPEG is 1200x630, at most 111 KB
- A sample crawl over a faked reveal season:
/and/playstyles/lore-denialname their own/og/page/image in both tags with alt text,/revealsnames its own, and/browse,/playstyles,/ink/amberand/votename the home image - Planted fault: deleting
playstyle-lore-denial.*.jpgmakes check 13 fail, naming that page - A sheet of all 26 images, and
/revealswith five reveals, two reveals, and Locations among the newest, looked right to me. It goes to the owner before merge. - No app JavaScript or card data changes
After deploy: curl the og:image of / and /playstyles/bounce, check one image's headers, and Scrape Again on / in Facebook's Sharing Debugger.
Generated by Claude Code
Summary by cubic
Gives the home, playstyle, and /reveals pages their own link preview images, so links to them no longer show the site-wide wordmark banner. Every other page the crawl writes now previews with the new home image.
- The deploy build draws each image (home, all 25 playstyles, and
/revealsin reveal season only) from a new template and pure helper; featured and playstyle cards from the next reveal set are drawn and counted too, preview-only cards read as the app's loader merges them. - Check 13 fails the deploy when a page names anything but its expected image, when
twitter:imagediffers fromog:image, when the file is missing from the build, or when a route the crawl should have written has no page at all. - Playstyle accents and reveal-set totals live in app TypeScript, so the helper parses
playstyleUi.tsandrevealSet.tsas text; their tests fail when either file stops parsing. - The crawl, image generator, and check 13 share the reveal-season test, so
/revealsis drawn and expected exactly when it is captured. withCardOgImageis renamedwithOgImage, since every page now uses it.