Skip to content

feat(site): pre.dev share card on every docs page - #62

Merged
arjunpredev merged 2 commits into
mainfrom
share-cards
Oct 26, 2026
Merged

arjunpredev merged 2 commits into
mainfrom
share-cards

Conversation

@arjunpredev

Copy link
Copy Markdown
Owner

Every docs page link now unfurls with the one pre.dev share card, showing that page's section, title and description.

How cards are made

  • site/lib/share-card.mjs is an exact copy of the card spec (header kept). site/lib/share-card.d.mts types it.
  • site/lib/og-card.ts builds each page's card:
    • eyebrow pre.dev · Docs · <section>
    • title (fit: true)
    • the page description
    • footer pre.dev documentation and docs.pre.dev/<page>
  • site/app/og/[...slug]/route.ts draws the cards at build time with satori 0.33.4 and @resvg/resvg-js 2.6.2, both added as devDependencies. These are the versions the spec was verified with. fitCard measures with that same satori. The route replaces the next/og ImageResponse card, which bundles satori 0.25 and gives no satori to measure with. A card that fails checkCard() stops the build.
  • Section: the page's group in docs.json, at most two levels below its tab. The AI Gateway endpoint pages read "AI Gateway reference" and the payments pages read "Payments". A section too long for the eyebrow line fails the build.
  • No description: the page's first paragraph is used. Today every page has a description, and npm run check already requires one.

Cache busting

  • Cards live at /og/<page>/<hash>.png. The hash is the first 10 hex characters of the sha256 of CARD_DESIGN plus the card's inputs.
  • When a title, description, section, address or design changes, the card gets a new URL, and the page's og:image and twitter:image point at it.
  • The Worker serves hashed cards with max-age=31536000, immutable.
  • /og/<page>/image.png, the old address, still answers with the current card, so earlier unfurls keep their image.

Tags on every page

og:image (+ :type, :width 1200, :height 630, :alt), twitter:card summary_large_image, twitter:image (+ :alt), og:site_name pre.dev, and og:title set to the page title alone. Both alt tags are cardAlt(card).

CI gate

npm run check:out (the "Docs site" workflow) now fails any nav page when:

  • its og:image is missing, or is not /og/<page>/<hash>.png
  • the PNG is missing from the build, or is not 1200x630
  • either alt tag is missing, the two differ, or the alt runs past 420 characters
  • twitter:card is not summary_large_image, or twitter:image differs from og:image
  • og:site_name is wrong, or og:title is more than the page title

Checked locally

  • npm run check: passed, 103 pages.
  • npm run build: 103 share cards. check:out: 103 pages, 103 share cards, 79 redirects, 1350 internal links OK.
  • check:worker: passed. tsc --noEmit: passed.
  • parity.mjs through wrangler dev: 404 URL checks, no differences.
  • The coding-agent/memory card is byte-identical to the approved reference render.
  • Negative test: removing one page's card PNG and another page's alt tags made check:out fail on both.

🤖 Generated with Claude Code

arjunpredev and others added 2 commits October 26, 2026 14:02
Every page's og:image and twitter:image is now the one pre.dev card design
(site/lib/share-card.mjs, an exact copy of the card spec), filled with the
page's section, title and description and the docs.pre.dev/<page> address.

- app/og/[...slug]/route.ts draws each card at build time with satori 0.33.4
  and @resvg/resvg-js (fitCard measures with the same satori), replacing the
  next/og ImageResponse card. checkCard() failures stop the build.
- Cards live at /og/<page>/<hash>.png. The hash is sha256 of CARD_DESIGN and
  the card's inputs, so a changed title, description, section or design gets a
  new URL. The Worker serves hashed cards as immutable; /og/<page>/image.png
  keeps answering with the current card.
- Tags: og:image with type, width, height and alt (cardAlt), twitter:image
  with alt, twitter:card summary_large_image, og:site_name pre.dev, og:title
  the page title alone.
- Section: the page's docs.json group, at most two levels below its tab.
  A page without a description uses its first paragraph.
- check:out fails a page without its card, its 1200x630 PNG, alt text or the
  full tag set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@arjunpredev
arjunpredev merged commit ec7d7e4 into main Oct 26, 2026
3 checks passed
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