feat: OG image sizing and responsive cover srcsets (#447)

* feat: add OG image sizing and responsive srcsets for cover images - Resize og_image to 1200x630 (Fill) in opengraph template with og:image:width/height meta tags for proper social preview rendering. - Replace single 1200x resize with 800x400 card + 1600x800 retina srcset in article-cover template for responsive cover images. Closes #358 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use JPEG for OG images and derive dimensions from resource Address Copilot review: WebP has spotty social preview support; switch to JPEG q85 for OG images. Derive width/height from the processed resource instead of hardcoding to prevent drift. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed Jun 13, 2026 at 07:37 UTC 2471f0f3e78c0a13f6ab3af07cd4785a2a019b68
2 files changed +16 -3
layouts/partials/templates/opengraph.html
+8 -1
@@ -36,15 +36,22 @@
36 {{- end }}
37
38 {{- $ogImage := "" -}}
39 +{{- $ogWidth := 0 -}}
40 +{{- $ogHeight := 0 -}}
41 {{- if and .Params.og_image (not (hasPrefix .Params.og_image "http://")) (not (hasPrefix .Params.og_image "https://")) (not (hasPrefix .Params.og_image "//")) -}}
42 {{- with resources.Get .Params.og_image -}}
41 - {{- $ogImage = .Permalink -}}
43 + {{- $resized := .Fill "1200x630 jpg q85" -}}
44 + {{- $ogImage = $resized.Permalink -}}
45 + {{- $ogWidth = $resized.Width -}}
46 + {{- $ogHeight = $resized.Height -}}
47 {{- end -}}
48 {{- end -}}
49 {{- $coverImage := .Params.cover.image -}}
50 {{- $coverImageIsLocal := and $coverImage (not (hasPrefix $coverImage "http://")) (not (hasPrefix $coverImage "https://")) (not (hasPrefix $coverImage "//")) -}}
51 {{- if $ogImage -}}
52 <meta property="og:image" content="{{ $ogImage }}">
53 + <meta property="og:image:width" content="{{ $ogWidth }}">
54 + <meta property="og:image:height" content="{{ $ogHeight }}">
55 {{- else if $coverImageIsLocal -}}
56 {{- if (ne .Params.cover.relative true) }}
57 <meta property="og:image" content="{{ $coverImage | absURL }}">
layouts/partials/visuals/article-cover.html
+8 -2
@@ -40,8 +40,14 @@
40 {{- $alt := ($page.Params.cover.alt | default $page.Title | plainify) -}}
41 {{- $attribution := $page.Params.cover.attribution -}}
42 <figure class="article-cover article-cover--image">
43 - {{- $sized := $img.Resize "1200x webp q82" -}}
44 - <img class="article-cover__img" src="{{ $sized.RelPermalink }}" width="{{ $sized.Width }}" height="{{ $sized.Height }}" loading="eager" decoding="async" alt="{{ $alt }}">
43 + {{- $card := $img.Fill "800x400 webp q82" -}}
44 + {{- $card2x := $img.Fill "1600x800 webp q82" -}}
45 + <img class="article-cover__img"
46 + src="{{ $card.RelPermalink }}"
47 + srcset="{{ $card.RelPermalink }} 800w, {{ $card2x.RelPermalink }} 1600w"
48 + sizes="(min-width: 768px) 800px, 100vw"
49 + width="{{ $card.Width }}" height="{{ $card.Height }}"
50 + loading="eager" decoding="async" alt="{{ $alt }}">
51 {{- /* Render attribution as a safe inline subset (#329): allow simple
52 emphasis/links from Markdown, but strip any <img> the markdown
53 image syntax would emit so hotlinked imagery cannot appear here. */ -}}