feat: share image, metadata and icons that match the new site
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:
@@ -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");
|
||||
Reference in New Issue
Block a user