From a94c66f99a2e12a77bc6cd04a3996804877f1bb1 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:15:34 +0200 Subject: [PATCH] feat(meta): give Solon a real social preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Solon shipped no og:image, so every link to it rendered as a blank grey rectangle in Telegram, Slack and any DM. Adds the Next file-convention card (1200x630) using the site's own palette from globals.css. Also sets metadataBase, which is load-bearing rather than cosmetic: without it Next resolves the generated image to http://localhost:3000/opengraph-image — a tag that is present, looks configured, and is unfetchable by every scraper. The fallback is the real host, so a missing env var degrades to correct instead of to localhost. Verified by building and rendering: 200, image/png, 1200x630, and the homepage emits og:image=https://solon.orangecat.ch/opengraph-image. --- src/app/layout.tsx | 11 ++++- src/app/opengraph-image.tsx | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/app/opengraph-image.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index faa287e..cec0100 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -17,14 +17,23 @@ const spaceGrotesk = Space_Grotesk({ display: "swap", }); +/** + * Where this site actually serves. Next resolves the generated og:image against + * `metadataBase`; without it the tag is emitted as http://localhost:3000/... — + * present, plausible, and unfetchable by every social scraper. The fallback is + * the real host rather than localhost so a missing env var degrades to correct. + */ +const SITE_URL = process.env.NEXT_PUBLIC_APP_URL || "https://solon.orangecat.ch"; + export const metadata: Metadata = { + metadataBase: new URL(SITE_URL), title: "Solon — Bitcoin-Native Governance", description: "Radical transparency and cryptographic democracy for organizations.", icons: [{ rel: "icon", url: "/favicon.ico" }], openGraph: { title: "Solon", description: "Bitcoin-Native Governance for the Digital Age", - url: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000", + url: SITE_URL, siteName: "Solon", }, }; diff --git a/src/app/opengraph-image.tsx b/src/app/opengraph-image.tsx new file mode 100644 index 0000000..a5459cb --- /dev/null +++ b/src/app/opengraph-image.tsx @@ -0,0 +1,97 @@ +import { ImageResponse } from "next/og"; + +/** + * Social preview card, generated at request time by Next's file convention. + * + * Solon had no og:image at all, so every link to it — Telegram, Slack, a DM — + * rendered as a blank grey rectangle. 1200×630 is the canonical 1.91:1 card + * size all three of those scrapers crop to. + * + * Satori cannot read CSS custom properties, so the palette is repeated here as + * literals. globals.css remains the source of truth; these mirror it and are + * the only non-CSS consumer of the tokens. + */ + +export const runtime = "edge"; +export const alt = "Solon — Bitcoin-Native Governance"; +export const size = { width: 1200, height: 630 }; +export const contentType = "image/png"; + +// Mirrors --solon-dark / --solon-orange / --solon-bitcoin in src/app/globals.css +const INK = "#0F172A"; +const ORANGE = "#F97316"; +const BITCOIN = "#F7931A"; +const MUTED = "#94A3B8"; + +export default function OGImage() { + return new ImageResponse( + ( +
+
+
+
+ Solon +
+
+ +
+
+ Bitcoin-Native Governance +
+
+ Radical transparency and cryptographic democracy for organizations. +
+
+ +
+
+
solon.orangecat.ch
+
+
+ ), + { ...size }, + ); +}