feat: share image, metadata and icons that match the new site
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Successful in 6m28s
Build & Deploy / 🚀 Deploy (push) Successful in 17s
Build & Deploy / 🩺 Smoke Test (push) Successful in 4s
Build & Deploy / 🔔 Notify (push) Successful in 2s

The link preview used an old template with a blog description. The share
image is now a screenshot of the real hero (scripts/render-share-image.mjs),
served as Open Graph and Twitter image. Title and description come from one
place and are used by the page, the previews and the structured data. Adds an
apple touch icon and removes the old share-image template and its fonts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 23:33:02 +02:00
co-authored by Claude Opus 5.5
parent 007a691357
commit 123ab8e354
19 changed files with 128 additions and 362 deletions
+54
View File
@@ -0,0 +1,54 @@
// Renders the share image (Open Graph and Twitter) from the real hero, so it always
// matches the site. Run it after the hero headline or its design changes:
//
// pnpm --filter @mintel/web share-image (uses the live site)
// pnpm --filter @mintel/web share-image http://mintel.localhost/
//
// It drives the locally installed Chrome through Playwright and writes
// app/(site)/opengraph-image.png and app/(site)/twitter-image.png.
import { copyFileSync } from "node:fs";
import { chromium } from "playwright";
const url = process.argv[2] ?? "https://mintel.me/";
const out = "app/(site)/opengraph-image.png";
const browser = await chromium.launch({ channel: "chrome", headless: true });
const page = await browser.newPage({
viewport: { width: 1200, height: 630 },
deviceScaleFactor: 1,
});
await page.goto(url, { waitUntil: "load", timeout: 90000 });
await page.addStyleTag({
content: `
header, footer, #hero-cta, [class*="fixed"] { display: none !important; }
main > div > section:first-of-type > div.relative { min-height: 630px !important; height: 630px !important; padding: 0 72px !important; justify-content: center !important; max-width: none !important; }
main > div > section:first-of-type ul { display: none !important; }
main > div > section:first-of-type h1 { font-size: 150px !important; }
main > div > section:first-of-type p { font-size: 34px !important; max-width: 900px !important; margin-top: 36px !important; }
main > div > section:first-of-type .grid { margin-top: 0 !important; display: block !important; }
main > div > section:first-of-type h1 span:nth-of-type(2) { color: rgba(15, 23, 42, 0.2) !important; }
main > div > section:first-of-type > div.relative { padding-top: 34px !important; }
html, body { overflow: hidden !important; }
`,
});
await page.evaluate(() => {
const section = document.querySelector("main section");
const tag = document.createElement("div");
tag.textContent = "MARC MINTEL";
tag.style.cssText =
"position:absolute;left:72px;top:54px;z-index:20;font:700 15px ui-monospace,Menlo,monospace;letter-spacing:.3em;color:#0f172a";
const site = document.createElement("div");
site.textContent = "mintel.me";
site.style.cssText =
"position:absolute;right:72px;top:54px;z-index:20;font:500 15px ui-monospace,Menlo,monospace;letter-spacing:.2em;color:#64748b";
section.style.position = "relative";
section.append(tag, site);
window.scrollTo(0, 0);
});
await page.waitForTimeout(4500);
await page.screenshot({
path: out,
clip: { x: 0, y: 0, width: 1200, height: 630 },
});
await browser.close();
copyFileSync(out, "app/(site)/twitter-image.png");
console.log("written:", out, "and app/(site)/twitter-image.png");