refactor(images): adopt Hugo reflect.IsImageResource* API for type-safe image processing

Replace manual format detection (printf "%T", MediaType.SubType whitelists,
duck-typing) with Hugo's new reflect.IsResource, reflect.IsImageResource, and
reflect.IsImageResourceProcessable functions across 19 template files.

Key changes:
- process_responsive_image: collapse 15-line format whitelist into single
  reflect.IsImageResourceProcessable call
- Unify three-way SVG/processable/GIF branches into two-way $canProcess guard
- Remove duplicate GIF passthrough branches in single.html, events/page.html
- Add missing SVG guard in research-areas block (was calling .Fill unprotected)
- Replace nil checks with reflect.IsResource in get_cover_image/get_featured_image

Design decision: GIFs are intentionally excluded from processing despite Hugo
supporting it — processing strips animation frames, so we preserve user-optimized
animated GIFs by checking (IsImageResourceProcessable AND not GIF).
This commit is contained in:
George Cushen committed 2026-03-27 04:35:53 +00:00
1 parent ee683c8cd1
commit 76023f9461
19 files changed
+135 -170

No files matched your search

+20 -20
View File
@@ -149,13 +149,13 @@
{{ $img := $image_resource }}
{{ if $img }}
{{ $isSVG := eq $img.MediaType.SubType "svg" }}
{{ if not $isSVG }}
{{ $isProcessable := reflect.IsImageResourceProcessable $img }}
{{ if $isProcessable }}
{{ $img = $img.Fit "280x140 webp" }}
{{ end }}
<img src="{{ $img.RelPermalink }}"
<img src="{{ $img.RelPermalink }}"
alt="{{ $name | default "Partner logo" }}"
{{ if not $isSVG }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
{{ if $isProcessable }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
class="h-12 sm:h-14 lg:h-16 w-auto mx-auto object-contain filter grayscale opacity-60 group-hover:grayscale-0 group-hover:opacity-100 transition-all duration-300 group-hover:scale-110"
loading="lazy">
{{ else if $image_url }}
@@ -191,13 +191,13 @@
<div class="group relative">
<div class="block p-4 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-all duration-300">
{{ $image := . }}
{{ $isSVG := eq $image.MediaType.SubType "svg" }}
{{ if not $isSVG }}
{{ $isProcessable := reflect.IsImageResourceProcessable $image }}
{{ if $isProcessable }}
{{ $image = $image.Fit "280x140 webp" }}
{{ end }}
<img src="{{ $image.RelPermalink }}"
<img src="{{ $image.RelPermalink }}"
alt="Partner logo"
{{ if not $isSVG }}width="{{ $image.Width }}" height="{{ $image.Height }}"{{ end }}
{{ if $isProcessable }}width="{{ $image.Width }}" height="{{ $image.Height }}"{{ end }}
class="h-12 sm:h-14 lg:h-16 w-auto mx-auto object-contain filter grayscale opacity-60 hover:grayscale-0 hover:opacity-100 transition-all duration-300 hover:scale-110"
loading="lazy">
</div>
@@ -234,13 +234,13 @@
{{ if .image }}
{{ $img := resources.Get (printf "media/%s" .image) }}
{{ if $img }}
{{ $isSVG := eq $img.MediaType.SubType "svg" }}
{{ if not $isSVG }}
{{ $isProcessable := reflect.IsImageResourceProcessable $img }}
{{ if $isProcessable }}
{{ $img = $img.Fit "280x140 webp" }}
{{ end }}
<img src="{{ $img.RelPermalink }}"
<img src="{{ $img.RelPermalink }}"
alt="{{ .name | default "Partner logo" }}"
{{ if not $isSVG }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
{{ if $isProcessable }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
class="h-16 lg:h-20 w-auto object-contain opacity-70 hover:opacity-100 transition-opacity"
loading="lazy">
{{ end }}
@@ -254,13 +254,13 @@
{{ if .image }}
{{ $img := resources.Get (printf "media/%s" .image) }}
{{ if $img }}
{{ $isSVG := eq $img.MediaType.SubType "svg" }}
{{ if not $isSVG }}
{{ $isProcessable := reflect.IsImageResourceProcessable $img }}
{{ if $isProcessable }}
{{ $img = $img.Fit "280x140 webp" }}
{{ end }}
<img src="{{ $img.RelPermalink }}"
<img src="{{ $img.RelPermalink }}"
alt="{{ .name | default "Partner logo" }}"
{{ if not $isSVG }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
{{ if $isProcessable }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
class="h-16 lg:h-20 w-auto object-contain opacity-70 hover:opacity-100 transition-opacity"
loading="lazy">
{{ end }}
@@ -286,13 +286,13 @@
{{ if $image_name }}
{{ $img := resources.Get (printf "media/%s" $image_name) }}
{{ if $img }}
{{ $isSVG := eq $img.MediaType.SubType "svg" }}
{{ if not $isSVG }}
{{ $isProcessable := reflect.IsImageResourceProcessable $img }}
{{ if $isProcessable }}
{{ $img = $img.Fit "280x140 webp" }}
{{ end }}
<img src="{{ $img.RelPermalink }}"
<img src="{{ $img.RelPermalink }}"
alt="{{ $name | default "Partner logo" }}"
{{ if not $isSVG }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
{{ if $isProcessable }}width="{{ $img.Width }}" height="{{ $img.Height }}"{{ end }}
class="h-14 lg:h-16 w-auto object-contain opacity-70"
loading="lazy">
{{ end }}
+3 -3
View File
@@ -277,9 +277,9 @@
<div class="relative h-48 overflow-hidden">
{{ $image := $item.Resources.GetMatch "{featured,cover,thumbnail}*" }}
{{ if $image }}
{{ if strings.Contains $image.MediaType.SubType "svg" }}
<img
src="{{ $image.RelPermalink }}"
{{ if not (reflect.IsImageResourceProcessable $image) }}
<img
src="{{ $image.RelPermalink }}"
alt="{{ $item.Title }}"
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
>
+3 -1
View File
@@ -211,7 +211,9 @@
{{ $img := $image_resource }}
{{ if $img }}
{{ $img = $img.Fill "600x400 Center webp" }}
{{ if reflect.IsImageResourceProcessable $img }}
{{ $img = $img.Fill "600x400 Center webp" }}
{{ end }}
{{ $alt := $name | plainify }}
{{ $header_content = printf "<img src=\"%s\" alt=\"%s\" class=\"w-full h-full object-cover opacity-80 group-hover:opacity-100 group-hover:scale-110 transition-all duration-500\">" $img.RelPermalink $alt }}
{{ else if $image_url }}
@@ -131,10 +131,10 @@
{{ $img = $image_resource }}
{{ if $img }}
{{ $isSVG := eq $img.MediaType.SubType "svg" }}
{{ $isGIF := eq $img.MediaType.SubType "gif" }}
{{/* reflect.IsImageResourceProcessable → false for SVG; GIF skipped to preserve animation */}}
{{ $canProcess := and (reflect.IsImageResourceProcessable $img) (ne $img.MediaType.SubType "gif") }}
<div class="relative w-full h-64 rounded-2xl overflow-hidden shadow-xl mb-8">
{{ if not (or $isSVG $isGIF) }}
{{ if $canProcess }}
{{ $responsive := partial "functions/process_responsive_image.html" (dict
"image" $img
"mode" "fill"
@@ -147,7 +147,7 @@
src="{{ $responsive.fallback.RelPermalink }}"
alt="" />
{{ else }}
{{ if not $isSVG }}{{ $img = $img.Process "webp" }}{{ end }}
{{/* SVG or GIF: pass through without responsive processing */}}
<img class="absolute inset-0 w-full h-full object-cover"
src="{{$img.RelPermalink}}"
alt="" />
@@ -103,9 +103,9 @@
{{ $img = $image_resource }}
{{ if $img }}
{{ $isSVG := eq $img.MediaType.SubType "svg" }}
{{ $isGIF := eq $img.MediaType.SubType "gif" }}
{{ if not (or $isSVG $isGIF) }}
{{/* reflect.IsImageResourceProcessable → false for SVG; GIF skipped to preserve animation */}}
{{ $canProcess := and (reflect.IsImageResourceProcessable $img) (ne $img.MediaType.SubType "gif") }}
{{ if $canProcess }}
{{ $responsive := partial "functions/process_responsive_image.html" (dict
"image" $img
"mode" "fill"
@@ -126,8 +126,7 @@
</div>
</div>
{{ else }}
{{/* Handle SVG/GIF without processing */}}
{{ if not $isSVG }}{{ $img = $img.Process "webp" }}{{ end }}
{{/* SVG or GIF: pass through without responsive processing */}}
<div class="w-full bg-gray-200 dark:bg-gray-900 flex flex-wrap items-center justify-center">
<div class="w-full bg-white rounded dark:bg-gray-800">
<div class="h-2/4 sm:h-64 overflow-hidden w-full">
@@ -27,9 +27,13 @@
<div class="flex justify-center ">
<div class="w-full" {{ with $max_width }}style="max-width: {{.}}"{{end}}>
{{- if $img -}}
{{- $isSVG := eq $img.MediaType.SubType "svg" -}}
{{- $isGIF := eq $img.MediaType.SubType "gif" -}}
{{- if $isSVG | or $isGIF -}}
{{/*
Image processing gate:
- reflect.IsImageResourceProcessable → false for SVG (not processable by Hugo).
- GIF: processable by Hugo but we skip to preserve animation frames.
*/}}
{{- $canProcess := and (reflect.IsImageResourceProcessable $img) (ne $img.MediaType.SubType "gif") -}}
{{- if not $canProcess -}}
<img alt="{{ $alt }}"
src="{{ $img.RelPermalink }}"
loading="lazy"
@@ -95,10 +95,14 @@
{{ $img_data := dict }}
{{ if $image_resource }}
{{ $isSVG := eq $image_resource.MediaType.SubType "svg" }}
{{ $isGIF := eq $image_resource.MediaType.SubType "gif" }}
{{ if or $isSVG $isGIF }}
{{/* SVG/GIF: no processing, just pass through */}}
{{/*
Image processing gate:
- reflect.IsImageResourceProcessable → false for SVG (not processable by Hugo).
- GIF: processable by Hugo but we skip to preserve animation frames.
*/}}
{{ $canProcess := and (reflect.IsImageResourceProcessable $image_resource) (ne $image_resource.MediaType.SubType "gif") }}
{{ if not $canProcess }}
{{/* SVG or GIF: no processing, just pass through */}}
{{ $img_data = dict "src" $image_resource.RelPermalink }}
{{ else if eq $block_type "cta-image-paragraph" }}
{{/* Responsive mode: generate srcset via process_responsive_image */}}
@@ -70,10 +70,15 @@
{{ $cover_height = 1080 }}
{{ end }}
{{/* Generate responsive images */}}
{{/*
Generate responsive images.
- reflect.IsImageResourceProcessable → false for SVG (not processable by Hugo).
- GIF: processable by Hugo but we skip to preserve animation frames.
*/}}
{{ $processed_cover := $cover }}
{{ $srcset := "" }}
{{ if ne $cover.MediaType.SubType "gif" }}
{{ $canProcess := and (reflect.IsImageResourceProcessable $cover) (ne $cover.MediaType.SubType "gif") }}
{{ if $canProcess }}
{{ $fill_spec := printf "%dx%d Center q85 webp" $cover_width $cover_height }}
{{ $processed_cover = $cover.Fill $fill_spec }}
@@ -110,8 +115,8 @@
<div class="absolute inset-0 overflow-hidden" style="{{ $mask_style | safeCSS }}">
{{/* Cover Image */}}
<div class="absolute inset-0">
{{ if ne $cover.MediaType.SubType "gif" }}
<img
{{ if $canProcess }}
<img
srcset="{{ $srcset }}"
sizes="100vw"
src="{{ $processed_cover.RelPermalink }}"
@@ -11,7 +11,7 @@
{{/* Search for an image "*cover*" in page folder */}}
{{ $resource := (.Resources.ByType "image").GetMatch "*cover*" }}
{{ if eq $resource nil }}
{{ if not (reflect.IsResource $resource) }}
{{/* Otherwise fall back to the image file specified in front matter */}}
{{ $filename := "" }}
{{ if .Params.cover }}
@@ -25,15 +25,20 @@
{{ if $filename }}
{{/* Check if it's a remote URL */}}
{{ if or (strings.HasPrefix $filename "http://") (strings.HasPrefix $filename "https://") }}
{{ $resource = resources.GetRemote $filename }}
{{ if eq $resource nil }}
{{ with resources.GetRemote $filename }}
{{ if reflect.IsResource . }}
{{ $resource = . }}
{{ else }}
{{ warnf "Remote cover image not found: %s" $filename }}
{{ end }}
{{ else }}
{{ warnf "Remote cover image not found: %s" $filename }}
{{ end }}
{{ else }}
{{/* Search in page folder */}}
{{ $resource = (.Resources.ByType "image").GetMatch $filename }}
{{/* Otherwise in `assets/media/` folder */}}
{{ if eq $resource nil }} {{ $resource = resources.GetMatch (path.Join "media" $filename) }} {{ end }}
{{ if not (reflect.IsResource $resource) }} {{ $resource = resources.GetMatch (path.Join "media" $filename) }} {{ end }}
{{ end }}
{{ end }}
{{ end }}
@@ -11,7 +11,7 @@
{{/* Search for an image "*featured*" in page folder */}}
{{ $resource := (.Resources.ByType "image").GetMatch "*featured*" }}
{{ if eq $resource nil }}
{{ if not (reflect.IsResource $resource) }}
{{/* Otherwise fall back the image file specified in front matter */}}
{{ $filename := "" }}
{{ if and .Params.image (reflect.IsMap .Params.image) }}
@@ -20,7 +20,7 @@
{{/* Search in page folder */}}
{{ $resource = (.Resources.ByType "image").GetMatch $filename }}
{{/* Otherwise in `assets/media/` folder */}}
{{ if eq $resource nil }} {{ $resource = resources.GetMatch (path.Join "media" $filename) }} {{ end }}
{{ if not (reflect.IsResource $resource) }} {{ $resource = resources.GetMatch (path.Join "media" $filename) }} {{ end }}
{{ end }}
{{ return $resource }}
@@ -174,7 +174,7 @@
{{ if $bg.image.filename }}
{{ $bg_img := resources.Get (printf "media/%s" $bg.image.filename) }}
{{ if $bg_img }}
{{ if ne $bg_img.MediaType.SubType "svg" }}
{{ if reflect.IsImageResourceProcessable $bg_img }}
{{ $bg_img = $bg_img.Fit "1920x1920 webp" }}
{{ end }}
{{ $image_layer := printf "url('%s')" $bg_img.RelPermalink }}
@@ -32,29 +32,21 @@ Returns: Map with processed images and srcset string
{{ $srcset_parts := slice }}
{{ $fallback_image := "" }}
{{/* Check if image supports processing - only allow known processable formats */}}
{{ $can_process := false }}
{{ if $image.MediaType }}
{{ $format := $image.MediaType.SubType }}
{{ $isJPEG := eq $format "jpeg" }}
{{ $isPNG := eq $format "png" }}
{{ $isWebP := eq $format "webp" }}
{{ $can_process = or $isJPEG $isPNG $isWebP }}
{{ else }}
{{/* Try detecting by file extension if MediaType not available */}}
{{ $ext := path.Ext $image.RelPermalink | lower }}
{{ $isJPEG := or (eq $ext ".jpg") (eq $ext ".jpeg") }}
{{ $isPNG := eq $ext ".png" }}
{{ $isWebP := eq $ext ".webp" }}
{{ $can_process = or $isJPEG $isPNG $isWebP }}
{{ end }}
{{/*
Processability check using Hugo's reflect API:
- reflect.IsImageResourceProcessable: true for JPEG, PNG, WebP, GIF, TIFF, BMP — false for SVG.
- GIF exclusion: Hugo *can* process GIFs, but doing so strips animation frames.
We intentionally skip GIF processing to preserve user-optimized animated GIFs.
*/}}
{{ $isProcessable := reflect.IsImageResourceProcessable $image }}
{{ $isGIF := and $isProcessable (eq $image.MediaType.SubType "gif") }}
{{ if $can_process }}
{{ if and $isProcessable (not $isGIF) }}
{{/* Handle responsive mode - for already processed images */}}
{{ if eq $mode "responsive" }}
{{/* Convert to WebP if needed */}}
{{ $base_image := $image }}
{{ if ne $image.MediaType.SubType "gif" }}{{ $base_image = $image.Process "webp" }}{{ end }}
{{ $base_image = $image.Process "webp" }}
{{/* Create responsive variants by resizing down from the processed image */}}
{{ range $width := $sizes }}
@@ -138,7 +130,7 @@ Returns: Map with processed images and srcset string
{{- end -}}
{{ end }}
{{- else -}}
{{/* Handle unsupported formats (SVG, GIF) - skip srcset as width/height are unavailable for SVGs */}}
{{/* Non-processable (SVG) or intentionally skipped (GIF): pass through without srcset */}}
{{- $fallback_image = $image -}}
{{- $processed_images = $processed_images | append $image -}}
{{- end -}}
@@ -14,8 +14,7 @@
<div class="flex pt-12 pb-4">
{{ if $avatar }}
{{ $isSVG := eq $avatar.MediaType.SubType "svg" }}
{{ if $isSVG }}
{{ if not (reflect.IsImageResourceProcessable $avatar) }}
<img
class="mr-4 h-24 w-24 {{if ne $avatar_shape "square"}}rounded-full{{end}}"
src="{{ $avatar.RelPermalink }}"
@@ -202,8 +202,7 @@
{{/* See Hugo note on linking assets in styles: https://github.com/gohugoio/hugoThemes#common-permalink-issues */}}
{{ $bg_img := resources.Get (printf "media/%s" .Params.design.background.image.filename) }}
{{ if $bg_img }}
{{/* Exception for SVG as Hugo doesn't yet support image processing on SVG. */}}
{{ if ne $bg_img.MediaType.SubType "svg" }}
{{ if reflect.IsImageResourceProcessable $bg_img }}
{{ $bg_img = $bg_img.Fit "1920x1920 webp" }}
{{ end }}
<style>
+10 -15
View File
@@ -36,17 +36,23 @@
{{ end }}
{{ end }}
{{ with $resource }}
{{ if strings.Contains .MediaType.SubType "svg" }}
{{/*
Image processing gate:
- reflect.IsImageResourceProcessable → false for SVG (not processable by Hugo).
- GIF: processable by Hugo but we skip to preserve animation frames.
*/}}
{{ $canProcess := and (reflect.IsImageResourceProcessable .) (ne .MediaType.SubType "gif") }}
{{ if not $canProcess }}
<a href="{{ $link }}" {{ $target | safeHTMLAttr }} class="block">
<img class="w-full h-full transition-transform duration-500 ease-out group-hover:scale-105 {{ if $fill_image }}object-fill{{ else }}object-contain{{ end }}"
<img class="w-full h-full transition-transform duration-500 ease-out group-hover:scale-105 {{ if $fill_image }}object-fill{{ else }}object-contain{{ end }}"
src="{{ .RelPermalink }}"
loading="lazy"
loading="lazy"
decoding="async"
{{ if eq $index 0 }}fetchpriority="high"{{ end }}
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;"
alt="{{ $item.Title | plainify }} featured image">
</a>
{{ else if ne .MediaType.SubType "gif" }}
{{ else }}
{{ $original_image := "" }}
{{ if $fill_image }}
{{ $original_image = .Fill (printf "800x450 %s" $anchor) }}
@@ -72,17 +78,6 @@
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;"
alt="{{ $item.Title | plainify }} featured image">
</a>
{{ else }}
<a href="{{ $link }}" {{ $target | safeHTMLAttr }} class="block">
<img class="w-full h-full transition-transform duration-500 ease-out group-hover:scale-105 {{ if $fill_image }}object-fill{{ else }}object-contain{{ end }}"
src="{{ .RelPermalink }}"
width="{{ .Width }}"
height="{{ .Height }}"
loading="lazy"
decoding="async"
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;"
alt="{{ $item.Title | plainify }} featured image">
</a>
{{ end }}
{{ else }}
<!-- Fallback gradient using theme colors - make clickable for consistency -->
@@ -59,39 +59,34 @@
{{/* Slide Preview Image - 16:9 aspect ratio */}}
<a href="{{ $present_link }}" target="_blank" rel="noopener" class="block relative aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-zinc-800 dark:to-zinc-900 overflow-hidden" aria-label="{{ printf "%s %s" $present_fullscreen_label ($item.Title | plainify) }}">
{{ with $resource }}
{{ if strings.Contains .MediaType.SubType "svg" }}
<img
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
src="{{ .RelPermalink }}"
loading="lazy"
decoding="async"
alt="{{ $item.Title | plainify }}">
{{ else if ne .MediaType.SubType "gif" }}
{{ $image := .Fill "800x450 Center" }}
{{ $responsive := partial "functions/process_responsive_image.html" (dict
"image" $image
"mode" "responsive"
"sizes" (slice 400 600 800)
"formats" (slice "avif" "webp" "jpg")
) }}
<img
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
srcset="{{ $responsive.srcset }}"
sizes="(max-width: 768px) 100vw, 50vw"
src="{{ $responsive.fallback.RelPermalink }}"
width="{{ $image.Width }}"
height="{{ $image.Height }}"
loading="lazy"
decoding="async"
{{ if eq $index 0 }}fetchpriority="high"{{ end }}
alt="{{ $item.Title | plainify }}">
{{ else }}
<img
{{/* reflect.IsImageResourceProcessable → false for SVG; GIF skipped to preserve animation */}}
{{ $canProcess := and (reflect.IsImageResourceProcessable .) (ne .MediaType.SubType "gif") }}
{{ if not $canProcess }}
<img
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
src="{{ .RelPermalink }}"
loading="lazy"
decoding="async"
alt="{{ $item.Title | plainify }}">
{{ else }}
{{ $image := .Fill "800x450 Center" }}
{{ $responsive := partial "functions/process_responsive_image.html" (dict
"image" $image
"mode" "responsive"
"sizes" (slice 400 600 800)
"formats" (slice "avif" "webp" "jpg")
) }}
<img
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
srcset="{{ $responsive.srcset }}"
sizes="(max-width: 768px) 100vw, 50vw"
src="{{ $responsive.fallback.RelPermalink }}"
width="{{ $image.Width }}"
height="{{ $image.Height }}"
loading="lazy"
decoding="async"
{{ if eq $index 0 }}fetchpriority="high"{{ end }}
alt="{{ $item.Title | plainify }}">
{{ end }}
{{ else }}
{{/* Fallback: Decorative gradient with slide icon - smaller, cleaner */}}
+1 -2
View File
@@ -9,8 +9,7 @@
<div class="flex flex-col sm:flex-row sm:items-center gap-6">
<div class="flex-shrink-0">
{{ if $avatar }}
{{ $isSVG := eq $avatar.MediaType.SubType "svg" }}
{{ if $isSVG }}
{{ if not (reflect.IsImageResourceProcessable $avatar) }}
<img
class="h-32 w-32 sm:h-40 sm:w-40 {{ if ne $avatar_shape "square" }}rounded-full{{ end }}"
src="{{ $avatar.RelPermalink }}"
+9 -25
View File
@@ -170,9 +170,14 @@
{{ $sizes_attr = "(max-width: 480px) 100vw, (max-width: 640px) 90vw, (max-width: 720px) 80vw, 720px" }}
{{end}}
{{/* Process featured image using utility with placement-specific constraints */}}
{{ if strings.Contains $featured.MediaType.SubType "svg" }}
{{/* SVGs cannot be processed by Hugo (Fit/Fill). Render directly. */}}
{{/*
Image processing gate:
- reflect.IsImageResourceProcessable → false for SVG (not processable by Hugo).
- GIF: processable by Hugo but we skip to preserve animation frames.
*/}}
{{ $canProcess := and (reflect.IsImageResourceProcessable $featured) (ne $featured.MediaType.SubType "gif") }}
{{ if not $canProcess }}
{{/* SVG or GIF: render directly without responsive processing */}}
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16">
<div style="position: relative">
<img src="{{ $featured.RelPermalink }}"
@@ -184,7 +189,7 @@
{{ with $image_params.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
</div>
</div>
{{ else if ne $featured.MediaType.SubType "gif" }}
{{ else }}
{{/* Pre-fit image to placement constraints, then apply responsive processing */}}
{{ $fitted_image := $featured }}
{{ if eq $placement 2}}
@@ -215,27 +220,6 @@
{{ with $image_params.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
</div>
</div>
{{ else }}
{{/* Handle GIF - apply same placement constraints without responsive processing */}}
{{ $image := $featured }}
{{ if eq $placement 2}}
{{ $image = $featured.Fit "1200x2500" }}
{{else if eq $placement 3}}
{{ $image = $featured.Fit "2560x2560" }}
{{else}}
{{ $image = $featured.Fit "720x2500" }}
{{end}}
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16" style="max-width: {{$image.Width}}px; max-height: {{$image.Height}}px;">
<div style="position: relative">
<img src="{{ $image.RelPermalink }}"
width="{{ $image.Width }}"
height="{{ $image.Height }}"
alt="{{ with $image_params.alt_text }}{{.}}{{ end }}"
class="featured-image"
fetchpriority="high">
{{ with $image_params.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
</div>
</div>
{{ end }}
{{end}}
{{ end }}
+9 -26
View File
@@ -177,9 +177,14 @@
{{ $sizes_attr = "(max-width: 480px) 100vw, (max-width: 640px) 90vw, (max-width: 720px) 80vw, 720px" }}
{{end}}
{{/* Process featured image using utility with placement-specific constraints */}}
{{ if strings.Contains $featured.MediaType.SubType "svg" }}
{{/* SVGs cannot be processed by Hugo (Fit/Fill). Render directly. */}}
{{/*
Image processing gate:
- reflect.IsImageResourceProcessable → false for SVG (not processable by Hugo).
- GIF: processable by Hugo but we skip to preserve animation frames.
*/}}
{{ $canProcess := and (reflect.IsImageResourceProcessable $featured) (ne $featured.MediaType.SubType "gif") }}
{{ if not $canProcess }}
{{/* SVG or GIF: render directly without responsive processing */}}
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16">
<div style="position: relative">
<img src="{{ $featured.RelPermalink }}"
@@ -190,7 +195,7 @@
{{ with $image_params.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
</div>
</div>
{{ else if ne $featured.MediaType.SubType "gif" }}
{{ else }}
{{/* Pre-fit image to placement constraints, then apply responsive processing */}}
{{ $fitted_image := $featured }}
{{ if eq $placement 2}}
@@ -222,28 +227,6 @@
{{ with $image_params.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
</div>
</div>
{{ else }}
{{/* Handle GIF - apply same placement constraints without responsive processing */}}
{{ $image := $featured }}
{{ if eq $placement 2}}
{{ $image = $featured.Fit "1200x2500" }}
{{else if eq $placement 3}}
{{ $image = $featured.Fit "2560x2560" }}
{{else}}
{{ $image = $featured.Fit "720x2500" }}
{{end}}
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16" style="max-width: {{$image.Width}}px; max-height: {{$image.Height}}px;">
<div style="position: relative">
<img src="{{ $image.RelPermalink }}"
data-pagefind-meta="image[src]"
width="{{ $image.Width }}"
height="{{ $image.Height }}"
alt="{{ with $image_params.alt_text }}{{.}}{{ end }}"
class="featured-image"
fetchpriority="high">
{{ with $image_params.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
</div>
</div>
{{ end }}
{{end}}