social: replace Hatch template og-image + wire per-interactive images

- New 1200×630 og-image.png with the editorial palette, a kasuti-style
  snowflake motif, and the correct title/tagline.
- Fixed the title-template suffix "| Hatch" → "| Interactives" in
  BaseHead.
- InteractiveLayout now checks public/og/<slug>.png at build time and
  passes it through as the preview image; falls back to the default
  otherwise. Ships a dedicated og/kasuti.png showing the front/back
  snowflake paired up.

https://claude.ai/code/session_01KNdXjQczMCRGMK7K3Qderw
This commit is contained in:
Claude 2026-04-22 10:48:00 +00:00
parent 6139278fbc
commit 70513860f7
No known key found for this signature in database
4 changed files with 10 additions and 2 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

BIN
public/og/kasuti.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -17,7 +17,7 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const { title, description, image } = Astro.props; const { title, description, image } = Astro.props;
const finalTitle = const finalTitle =
title && title !== SITE_METADATA.title.default title && title !== SITE_METADATA.title.default
? `${title} | Hatch` ? `${title} | Interactives`
: title || SITE_METADATA.title.default; : title || SITE_METADATA.title.default;
const finalDescription = description || SITE_METADATA.description; const finalDescription = description || SITE_METADATA.description;
const finalImage = image || SITE_METADATA.openGraph.images[0].url; const finalImage = image || SITE_METADATA.openGraph.images[0].url;

View file

@ -1,4 +1,7 @@
--- ---
import fs from "node:fs";
import path from "node:path";
import BaseHead from "@/components/BaseHead.astro"; import BaseHead from "@/components/BaseHead.astro";
import { Footer } from "@/components/sections/footer"; import { Footer } from "@/components/sections/footer";
import Navbar from "@/components/sections/navbar"; import Navbar from "@/components/sections/navbar";
@ -21,11 +24,16 @@ interface Props {
const { title, description, slug, themes, subcategory, tags, status } = Astro.props; const { title, description, slug, themes, subcategory, tags, status } = Astro.props;
const currentUrl = new URL(Astro.url.pathname, Astro.site).toString(); const currentUrl = new URL(Astro.url.pathname, Astro.site).toString();
const embedUrl = new URL(`/embed/${slug}`, Astro.site).toString(); const embedUrl = new URL(`/embed/${slug}`, Astro.site).toString();
// If public/og/<slug>.png exists, use it as the preview card image; otherwise the
// site-wide default in BaseHead applies.
const ogCandidate = path.join(process.cwd(), "public", "og", `${slug}.png`);
const ogImage = fs.existsSync(ogCandidate) ? `/og/${slug}.png` : undefined;
--- ---
<html lang="en"> <html lang="en">
<head> <head>
<BaseHead title={title} description={description} /> <BaseHead title={title} description={description} image={ogImage} />
</head> </head>
<body class="flex min-h-screen flex-col antialiased"> <body class="flex min-h-screen flex-col antialiased">
<Navbar client:load /> <Navbar client:load />