Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion assets/scss/partials/article.scss
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@
border-radius: var(--card-border-radius);
overflow: hidden;
position: relative;
height: 350px;
width: 250px;
height: 150px;
box-shadow: var(--shadow-l1);
transition: box-shadow 0.3s ease;
background-color: var(--card-background);
Expand Down
2 changes: 0 additions & 2 deletions assets/scss/partials/layout/article.scss
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,6 @@
margin-right: 15px;
flex-shrink: 0;
overflow: hidden;
width: 250px;
height: 150px;

.article-title {
font-size: 1.8rem;
Expand Down
2 changes: 0 additions & 2 deletions assets/scss/partials/layout/list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@
width: max-content;

article {
width: 250px;
height: 150px;
margin-right: 20px;
flex-shrink: 0;

Expand Down
10 changes: 6 additions & 4 deletions config/_default/params.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ SortBy = "default"
toggle = true
default = "auto"

[imageProcessing.cover]
enabled = true
[imageProcessing]
[imageProcessing.content]
widths = [800, 1600, 2400]
enabled = true

[imageProcessing.content]
enabled = true
[imageProcessing.thumbnail]
enabled = true

# GDPR Cookie Consent Configuration
# When enabled, analytics and functional cookies require user consent
Expand Down
42 changes: 24 additions & 18 deletions layouts/_markup/render-image.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@
{{- $alt := .PlainText | safeHTML -}}
{{- $title := .Title | markdownify -}}

{{/* SVG and external images won't work with gallery layout, because their width and height attributes are unknown */}}
{{/* There are some types of images that don't have width and height attributes, such as SVG */}}
{{- $galleryImage := and $image.Height $image.Width -}}

<img src="{{ $image.Permalink }}"
{{ with $image.Width }}width="{{ . }}"{{ end }}
{{ with $image.Height }}height="{{ . }}"{{ end }}
loading="lazy"
{{ with $alt }}
alt="{{ . }}"
{{ end }}
{{ with $title }}
title="{{ . }}"
data-title-escaped="{{ . | htmlEscape }}"
{{ end }}
{{ if $galleryImage }}
class="gallery-image"
data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"
data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}
>
{{- $attributes := dict -}}
{{- if $galleryImage -}}
{{- $attributes = (dict
"data-flex-grow" (div (mul $image.Width 100) $image.Height)
"data-flex-basis" (printf "%dpx" (div (mul $image.Width 240) $image.Height))
) -}}
{{- end -}}

{{ partial "helper/responsive-image" (dict
"Resource" $image.Resource
"Widths" (cond .Page.Site.Params.ImageProcessing.Content.Enabled .Page.Site.Params.ImageProcessing.Content.Widths nil)
"Attributes" (merge $attributes (dict
"src" $image.Permalink
"width" $image.Width
"height" $image.Height
"alt" $alt
"title" $title
"data-title-escaped" ($title | htmlEscape)
"class" (cond $galleryImage "gallery-image" "")
"loading" "lazy"
"sizes" "(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px"
))
Comment on lines +19 to +29
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data-title-escaped attribute is no longer rendered on gallery images in the new responsive-image helper, but gallery.ts still reads this attribute (lines 77–78) to populate figcaptions via innerHTML. Without it, the gallery caption falls back to the alt attribute text instead of the (possibly markdown-formatted) image title.

In the old code, the data-title-escaped attribute held the HTML-entity-escaped version of the markdownified title, which was required for safe assignment to innerHTML in JS. Now that neither data-title-escaped nor any equivalent attribute is emitted, any image with a title in Markdown (e.g. ![alt](url "title")) will silently lose its gallery caption.

The fix would be to include a "data-title-escaped" key in the attributes dict passed to responsive-image, set to the HTML-escaped form of $title when $title is non-empty.

Copilot uses AI. Check for mistakes.
) }}
15 changes: 13 additions & 2 deletions layouts/_partials/article-list/compact.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ <h2 class="article-title">
{{- $image := partial "helper/image" (dict "Image" .Params.image "Resources" .Resources) -}}
{{ if $image }}
<div class="article-image">
<img src="{{ $image.Permalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" alt="{{ .Title }}"
loading="lazy">
{{ partial "helper/thumbnail-image" (dict
"Resource" $image.Resource
"Width" 60
"Height" 60
"Resize" .Site.Params.ImageProcessing.Thumbnail.Enabled
"Attributes" (dict
"src" $image.Permalink
"alt" .Title
"loading" "lazy"
"width" $image.Width
"height" $image.Height
)
) }}
</div>
{{ end }}
</a>
Expand Down
15 changes: 13 additions & 2 deletions layouts/_partials/article-list/tile.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@
<a href="{{ .context.RelPermalink }}">
{{ if $image }}
<div class="article-image">
<img src="{{ $image.Permalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" loading="lazy"
alt="Featured image of post {{ .context.Title }}">
{{ partial "helper/thumbnail-image" (dict
"Resource" $image.Resource
"Width" 250
"Height" 150
"Resize" .context.Site.Params.ImageProcessing.Thumbnail.Enabled
"Attributes" (dict
"src" $image.Permalink
"alt" (printf "Featured image of post %s" .context.Title)
"loading" "lazy"
"width" $image.Width
"height" $image.Height
)
) }}
</div>
{{ end }}

Expand Down
14 changes: 12 additions & 2 deletions layouts/_partials/article/components/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@
{{ if $image }}
<div class="article-image">
<a href="{{ $Page.RelPermalink }}">
<img src="{{ $image.Permalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" loading="lazy"
alt="Featured image of post {{ $Page.Title }}" />
{{ partial "helper/responsive-image" (dict
"Resource" $image.Resource
"Widths" (cond $Page.Site.Params.ImageProcessing.Content.Enabled $Page.Site.Params.ImageProcessing.Content.Widths nil)
"Attributes" (dict
"src" $image.Permalink
"width" $image.Width
"height" $image.Height
"alt" (printf "Featured image of post %s" $Page.Title)
"loading" "lazy"
"sizes" "(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px"
)
) }}
</a>
</div>
{{ end }}
Expand Down
33 changes: 33 additions & 0 deletions layouts/_partials/helper/responsive-image.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{{- /*
Params:
Resource: Hugo image resource object (Optional, for processing)
Widths: Slice of widths to generate for srcset
Attributes: Map of HTML attributes for the <img> tag
*/ -}}

{{- $resource := .Resource -}}
{{- $widths := .Widths -}}
{{- $attributes := .Attributes | default dict -}}

{{/* Generate srcset if Resource and Widths are provided */}}
{{- if and $widths $resource (reflect.IsImageResourceProcessable $resource) -}}
{{- $srcset := slice -}}
{{- range $widths -}}
{{- if lt . $resource.Width -}}
{{- $resized := $resource.Resize (printf "%dx" .) -}}
{{- $srcset = $srcset | append (printf "%s %dw" $resized.RelPermalink .) -}}
{{- end -}}
{{- end -}}

{{- if gt (len $srcset) 0 -}}
{{- $permalink := default $resource.RelPermalink (index $attributes "src") -}}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The srcset entries from resized images use RelPermalink (relative paths), but the final "full resolution" entry at line 24 uses $permalink, which comes from (index $attributes "src"). When the caller passes an absolute URL as src (e.g., for a remote image), this results in a srcset that mixes relative paths for the resized variants with an absolute URL for the full-resolution entry. While modern browsers can handle this, the inconsistency can cause unexpected behavior. The $permalink could be normalized to $resource.RelPermalink when the resource exists and has been processed from a remote URL (i.e., the locally cached version's relative path), or at least kept consistent in type with the other entries.

Suggested change
{{- $permalink := default $resource.RelPermalink (index $attributes "src") -}}
{{- $permalink := $resource.RelPermalink -}}

Copilot uses AI. Check for mistakes.
{{- $srcset = $srcset | append (printf "%s %dw" $permalink $resource.Width) -}}
{{- $attributes = merge $attributes (dict "srcset" (delimit $srcset ", ")) -}}
{{- end -}}
{{- end -}}

<img {{- range $k, $v := $attributes -}}
{{- if $v -}}
{{- printf " %s=%q" (lower $k) (printf "%v" $v) | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>
41 changes: 41 additions & 0 deletions layouts/_partials/helper/thumbnail-image.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{{- /*
Params:
Resource: Hugo image resource object (Optional, for processing)
Width: Image width (Required)
Height: Image height (Required)
Resize: Whether to perform resize (Optional, default: false)
Attributes: Map of HTML attributes for the <img> tag
*/ -}}

{{- $resource := .Resource -}}
{{- $width := .Width -}}
{{- $height := .Height -}}
{{- $resize := .Resize -}}
{{- $attributes := .Attributes | default dict -}}

{{- if and $resize $resource (reflect.IsImageResourceProcessable $resource) $width $height -}}
{{/* Create thumbnail with 1x/2x descriptors */}}
{{- $srcset := slice -}}
{{- range (slice 1 2) -}}
{{- $w := mul $width . -}}
{{- $h := mul $height . -}}
{{- if and (le $w $resource.Width) (le $h $resource.Height) -}}
{{- $resized := $resource.Fill (printf "%dx%d" $w $h) -}}
{{- $srcset = $srcset | append (printf "%s %dx" $resized.RelPermalink .) -}}

{{- if eq . 1 -}}
{{- $attributes = merge $attributes (dict "src" $resized.RelPermalink) -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{- if gt (len $srcset) 0 -}}
{{- $attributes = merge $attributes (dict "width" $width "height" $height "srcset" (delimit $srcset ", ")) -}}
{{- end -}}
{{- end -}}

<img {{- range $k, $v := $attributes -}}
{{- if $v -}}
{{- printf " %s=%q" (lower $k) (printf "%v" $v) | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>