8000 feat(og-image-raster): fix social share preview (SVG -> PNG) by dominicusin · Pull Request #114 · dominicusin/dominicusin.github.io · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .beads/beads.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@
{ "id": "T29", "kind": "task", "title": "Write scripts/check-perf.cjs: parse built public/index.html, fail on render-blocking script/CSS", "status": "in_progress", "deps": [], "artifacts": ["scripts/check-perf.cjs"], "initiative": "perf-gate" },
{ "id": "T30", "kind": "task", "title": "Test check-perf.cjs: passes on current build; fails on injected blocking script/stylesheet (negative test, reverted)", "status": "pending", "deps": ["T29"], "artifacts": ["scripts/check-perf.cjs"], "initiative": "perf-gate" },
{ "id": "T31", "kind": "task", "title": "Wire Performance smoke check step into hugo.yml (report-only, after build)", "status": "done", "deps": ["T29", "T30"], "artifacts": [".github/workflows/hugo.yml"], "initiative": "perf-gate" },
{ "id": "T32", "kind": "task", "title": "Verify build/lint/test/linkcheck + commit+PR+admin-merge (perf-gate)", "status": "done", "deps&qu 8000 ot;: ["T31"], "pr": 113, "merged": "e4334ffee", "initiative": "perf-gate" }
{ "id": "T32", "kind": "task", "title": "Verify build/lint/test/linkcheck + commit+PR+admin-merge (perf-gate)", "status": "done", "deps": ["T31"], "pr": 113, "merged": "e4334ffee", "initiative": "perf-gate" },
{ "id": "T33", "kind": "task", "title": "Author og-default.svg (1200x630 brand) + generate assets/images/og-default.png via rsvg-convert", "status": "done", "deps": [], "artifacts": ["assets/images/og-default.png", "assets/images/og-default.src.svg"], "verified": "PNG 1200x630, 89KB; rsvg-convert reproducible", "initiative": "og-image-raster" },
{ "id": "T34", "kind": "task", "title": "Switch params.toml defaultSocialImage to images/og-default.png (keep card SVG)", "status": "done", "deps": ["T33"], "artifacts": ["config/_default/params.toml"], "initiative": "og-image-raster" },
{ "id": "T35", "kind": "task", "title": "Verify built og:image/twitter:image end in .png; PNG 1200x630 <250KB; build/lint/test/linkcheck green", "status": "done", "deps": ["T33", "T34"], "artifacts": ["public/index.html"], "verified": "og:image+twitter:image -> og-default.png; perf gate 0 regressions", "initiative": "og-image-raster" },
{ "id": "T36", "kind": "task", "title": "Commit + PR + gh pr merge --admin for og-image-raster", "status": "in_progress", "deps": ["T35"], "initiative": "og-image-raster" }
],
"edges": [
{ "from": "T2", "to": "T3", "type": "enables" },
Expand Down Expand Up @@ -66,6 +70,9 @@
{ "from": "T27", "to": "T28", "type": "blocks" },
{ "from": "T29", "to": "T30", "type": "enables" },
{ "from": "T30", "to": "T31", "type": "enables" },
{ "from": "T31", "to": "T32", "type": "blocks" }
{ "from": "T31", "to": "T32", "type": "blocks" },
{ "from": "T33", "to": "T34", "type": "enables" },
{ "from": "T34", "to": "T35", "type": "enables" },
{ "from": "T35", "to": "T36", "type": "blocks" }
]
}
27 changes: 27 additions & 0 deletions .gsd/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,30 @@ Beads T27 = `done`; T28 (commit+PR+merge for CSS fix) in_progress.

### Status
Beads T29–T31 = `done`; T32 (commit+PR+merge) in_progress.

---

## Initiative 7: `og-image-raster` (self-identified) — DONE
- **Self-identified problem (autonomous task-setting):** audit of built homepage
found `og:image`/`twitter:image` pointed at `images/og-default.svg`. Social
platforms (X/Telegram/Slack/Discord/FB/LinkedIn) do NOT render SVG previews →
sharing the URL shows NO image. Real, user-visible defect.
- **Fix (R1–R5):**
- Authored `assets/images/og-default.src.svg` (1200×630 brand: title + tagline on
`dominicusin` dark scheme) and converted to `assets/images/og-default.png`
(1200×630, 89KB) via `rsvg-convert`. Reproducible, no external service/secrets.
- Switched `config/_default/params.toml` `defaultSocialImage` → `images/og-default.png`.
The card-thumbnail SVG is left untouched (different role).
- **Root-cause gotcha (real, not guessed):** Blowfish's `head.html` emits the social
image via `resources.Get .Site.Params.defaultSocialImage` + `.RelPermalink`.
`resources.Get` resolves from the **assets** filesystem, not `static/`. The old
`.svg` worked only because `assets/images/og-default.svg` already existed (project
asset). Putting the PNG in `static/` made `resources.Get` return nil → NO og:image.
Fix: PNG placed in `assets/images/` (project asset overrides theme). Now both
`og:image` and `twitter:image` emit `…/og-default.png`.
- **Verify:** built `public/index.html` → `og:image` + `twitter:image` end in `.png`;
PNG 1200×630 / 89KB (<250KB); `hugo` 0 errors; lint clean; test pass; check-links 0
broken; check-perf 0 regressions.

### Status
Beads T33–T35 = `done`; T36 (commit+PR+merge) in_progress.
41 changes: 41 additions & 0 deletions .planning/initiatives/og-image-raster.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# BMAD — Initiative: `og-image-raster`

> Governance/reasoning only: WHY + SHAPE + locked decisions.
> Self-identified initiative (autonomous task-setting). Contract → Spec Kit.
> State → Beads. Execution → GSD.

## Why (reasoning)
Audit of the built homepage found `og:image` / `twitter:image` point at
`/images/og-default.svg`. **Social platforms (X/Twitter, Telegram, Slack,
Discord, Facebook, LinkedIn) do NOT render SVG as a link preview image** — they
require a raster format (PNG/JPEG). Result: sharing the site's URL shows NO preview
image. This is a real, user-visible defect (the whole point of OG/Twitter cards).
`robots.txt` and canonical/sitemap are fine, so this is the highest-value, ownable
hygiene fix.

## Shape (locked decisions)
1. **Do NOT break existing usage.** `og-default.svg` is also used as a decorative,
lazy-loaded thumbnail in content cards (`role=presentation`). Leave it there.
Create a SEPARATE raster asset for social sharing.
2. **Generate `static/images/og-default.png` at 1200×630** (Open Graph standard
dimensions, 1.91:1) with the site brand (name + tagline) on the `dominicusin`
dark scheme. Reproducible locally via `rsvg-convert` from a purpose-built
`og-default.svg` source (no external service, no secrets).
3. **Switch `config/_default/params.toml` `defaultSocialImage`** from the SVG to
`images/og-default.png`. Blowfish emits `og:image` + `twitter:image` +
`og:image:type`/`twitter:card` from this.
4. **Graceful & safe:** build stays 0-error; lint/test/linkcheck stay green; no
behavior change to cards or theme.
5. **Supply-chain note:** `rsvg-convert` is a build-time dev tool only (used to
produce the committed PNG); it is NOT a runtime dependency. The PNG is
committed to `static/images/` and served as a static asset.

## Out of scope
- Per-page OG images (only the site-wide default fallback is addressed; per-post
`featured`/`cover` images already work via Blowfish).
- Changing the card thumbnails (SVG stays there, works as decoration).

## Handoff
- → Spec Kit `.specify/og-image-raster.md`: R1–Rn + acceptance.
- → Beads `.beads/beads.json`: T33–T36.
- → GSD `.gsd/plan.md`: execution + evidence.
30 changes: 30 additions & 0 deletions .specify/og-image-raster.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Spec — OG/Twitter raster share image

> Spec Kit contract (the "what"). Binding for Beads (state) and GSD (execution).
> Governance: `.planning/initiatives/og-image-raster.md`.

## Context
`og:image`/`twitter:image` currently point at `images/og-default.svg`, which social
platforms cannot render as a link preview. Generate a raster (PNG) share image and
point the site default at it.

## Requirements
- **R1** New `static/images/og-default.png` exists at 1200×630 px (OG standard),
1.91:1, with the site brand (name + tagline) on the `dominicusin` dark scheme.
- **R2** Source is a reproducible `og-default.svg` (committed or generated) converted
via `rsvg-convert`; PNG is committed to `static/images/` (served statically).
- **R3** `config/_default/params.toml` `defaultSocialImage` = `images/og-default.png`
(was `images/og-default.svg`). The existing SVG is left for card thumbnails.
- **R4** Built homepage (and a sample post) emit `og:image` + `twitter:image` ending
in `.png`; `og:image:type`/`twitter:card` correct.
- **R5** PNG size is reasonable (< 250 KB) so previews load fast.
- **R6** Safety: `hugo` 0 errors; `npm run lint` clean; `npm run test` pass;
`node scripts/check-links.cjs` 0 broken; `node scripts/check-perf.cjs` 0 regressions.

## Acceptance (measurable)
- `file static/images/og-default.png` is a PNG, dimensions 1200×630, < 250 KB.
- Built `public/index.html` `og:image` value ends with `.png` (grep proves it).
- `defaultSocialImage` in params.toml = `images/og-default.png`.

## Out of scope
- Per-post featured images; card thumbnail SVG behavior.
Binary file added assets/images/og-default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions assets/images/og-default.src.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion config/_default/params.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ highlightCurrentMenuArea = true # highlight active menu item
smartTOC = true # scroll-spy table of contents

# --- Images ---
defaultSocialImage = "images/og-default.svg" # OG/Twitter fallback
defaultSocialImage = "images/og-default.png" # OG/Twitter fallback (raster; SVG is not rendered by social platforms)
defaultFeaturedImage = "images/og-default.svg" # card/hero fallback when a post has no image

# --- Subscription (Buttondown) ---
Expand Down
Loading
0