Skip to content

Dynamic Open Graph share cards for profiles, orgs & leaderboards#111

Open
peetzweg wants to merge 3 commits into
mainfrom
feature-dynamic-og-images
Open

Dynamic Open Graph share cards for profiles, orgs & leaderboards#111
peetzweg wants to merge 3 commits into
mainfrom
feature-dynamic-og-images

Conversation

@peetzweg

@peetzweg peetzweg commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Gives the DB-driven pages real share previews instead of everyone getting the one static site card.

Why: sharing a profile, an org, or the org leaderboard to X/Slack/LinkedIn currently shows the generic og.png — no signal about who or what board you're linking to.

How: per-entity cards render at runtime on /og/$kind/$login (satori + resvg, mirroring the existing /embed route and its durable-CDN headers); the two fixed leaderboard cards are baked at build time. All card drawing lives in one fs-free lib/og-card module shared by the build script and the route, so the build-time and runtime images stay identical. head() on /$user and / wires og:image to the right endpoint/card (org board picked by ?kind=org).

One judgment call: the developer card shows two ranks (overall + public-commits) and no commit count, per the brief. Non-latin/missing display names fall back to the @handle so nothing renders as tofu.

satori + @resvg/resvg-js moved to dependencies (server bundle needs them; the native binding traces into .output). Verified end-to-end against pnpm build && pnpm start. public/og/ is gitignored — cards regenerate on deploy (pnpm build, which the Coolify Dockerfile already runs).

Give the high-traffic pages their own share previews instead of the single
static site card:

- Developer pages (/$user): avatar, name, overall + public-commits rank.
- Organization pages (/paritytech): avatar, name, place on the org board.
- Leaderboards: distinct "Developer" vs "Organization" cards, picked by ?kind.

Per-entity cards render at runtime on /og/$kind/$login (satori + resvg, same
pattern and durable-CDN headers as /embed); the two leaderboard cards are fixed
content so they're baked at build time. All rendering lives in a shared,
fs-free lib/og-card module reused by the build script and the route.

- lib/og-card.ts: shared satori/resvg card builders (font + crown inlined).
- routes/og.$kind.$login.tsx: runtime PNG route; unknown/building logins 302
  to the static site card.
- lib/org.ts: orgRankFor() for a login's org-board place; export resolveOrg.
- generate-og.ts now also emits the two leaderboard cards; runs via tsx so it
  can import the shared #/ module (plain node can't resolve the TS paths).
- $user/index head(): wire og:image to the new endpoints/cards.
- satori + @resvg/resvg-js moved to dependencies (needed in the server bundle);
  tsx added as a devDependency.
@git-hub-peetzweg

git-hub-peetzweg Bot commented Jul 9, 2026

Copy link
Copy Markdown

The preview deployment for peetzweg/commit-history:coolify-bwc8csg4w8gkkgcg8s404cks is ready. 🟢

Open Preview | Open Build Logs | Open Application Logs

Last updated at: 2026-07-10 13:48:23 CET

peetzweg added 2 commits July 10, 2026 15:32
Per design feedback: rank numbers render in the ink colour, not the accent
green, on both cards. The developer card drops the overall rank and shows only
its public-commits place.
The single developer rank is the public-commits place; when the profile has no
public commits to rank, it falls back to the private-contributions place
("#N by private contributions") instead of dropping the line.
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