Compare commits

...
Author SHA1 Message Date
JimmyandGitHub 3dae60ac7a fix: use IsImageResourceWithMeta to check whether image has width / height attributes (#1285) 2026-03-05 09:23:47 +01:00
22edce91ab feat: add responsive image support (#1283)
* feat: create responsive-image helper

* feat: new responsive-image helper

* feat: Implement responsive image rendering in page content using a new partial and configure image processing widths.

* feat: add `sizes` attribute for enhanced image responsiveness

* feat: add responsive image to page header too

* Check for `.Enabled` before processing image

* style: remove wrong article-list--tile width and height

* feat: add `helper/thumbnail-image` for responsive thumbnails

* feat: enable content image processing and update responsive image `sizes` attributes for improved display.

* fix: get src from the attributes so external image can work correctly

* fix: add missing data-title-escaped

* Update layouts/_partials/article/components/header.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat: remove default `true` for thumbnail resize parameter.

* Update layouts/_markup/render-image.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update layouts/_partials/helper/thumbnail-image.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add original width and height to thumbnail pictures, for case that resize is not enabled

* Update layouts/_partials/helper/thumbnail-image.html

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-04 23:32:33 +01:00
JimmyandGitHub 78800c7379 fix: keep external image url, and use reflect.IsImageResourceProcessable to check for svg and other non-processable image formats (#1282)
* fix: keep external image url, and use reflect.IsImageResourceProcessable to check for svg

* build: update Hugo version to 0.157.0 in the build script.

* docs: remove `Local` return value and simplify `Resource` description in image helper

* Check for reflect.IsImageResourceProcessable before getting color from image

* chore: update minimum Hugo version to 0.157.0
2026-03-04 13:05:17 +01:00
a0bd0073bb fix: reading time not displaying in article/components/details partial (#1278)
Fix reading time not displaying due to .Site context in dict partial

The details.html partial receives a dict context via:
  partial "article/components/details" (dict "Page" $Page "IsList" $IsList)

When the dot context is a dict, .Site resolves to nil, so
.Site.Params.article.readingTime always returns empty/false,
causing the reading time to never display.

Lines 40 and 67 of the same file already correctly use
$Page.Site.Params — this fixes line 31 to be consistent.

Co-authored-by: delize <4028612+delize@users.noreply.github.com>
2026-03-04 11:58:09 +01:00
Jimmy Cai 3ca74956a6 ci: update Hugo version to 0.156.0 2026-02-19 17:54:42 +01:00
15 changed files with 165 additions and 54 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
main() {
DART_SASS_VERSION=1.97.1
GO_VERSION=1.25.5
HUGO_VERSION=0.154.5
HUGO_VERSION=0.157.0
NODE_VERSION=24.12.0
export TZ=Europe/Oslo
+1 -1
View File
@@ -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);
-2
View File
@@ -242,8 +242,6 @@
margin-right: 15px;
flex-shrink: 0;
overflow: hidden;
width: 250px;
height: 150px;
.article-title {
font-size: 1.8rem;
-2
View File
@@ -56,8 +56,6 @@
width: max-content;
article {
width: 250px;
height: 150px;
margin-right: 20px;
flex-shrink: 0;
+1 -1
View File
@@ -1,3 +1,3 @@
[hugoVersion]
extended = true
min = "0.154.0"
min = "0.157.0"
+6 -4
View File
@@ -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
+24 -18
View File
@@ -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"
))
) }}
+13 -2
View File
@@ -14,8 +14,19 @@
{{- $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>
+14 -3
View File
@@ -3,13 +3,24 @@
<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 }}
<div class="article-details"
{{- if and $image $image.Resource -}}
{{- if and $image (reflect.IsImageResourceProcessable $image.Resource) -}}
{{- $colors := $image.Resource.Colors -}}
{{- if $colors -}}
{{- $vibrant := index $colors 0 -}}
@@ -28,7 +28,7 @@
{{ end }}
</div>
{{ $showReadingTime := $Page.Params.readingTime | default (.Site.Params.article.readingTime) }}
{{ $showReadingTime := $Page.Params.readingTime | default ($Page.Site.Params.article.readingTime) }}
{{ $showDate := not $Page.Date.IsZero }}
{{ $showFooter := or $showDate $showReadingTime }}
{{ if $showFooter }}
@@ -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 }}
+17 -16
View File
@@ -3,8 +3,7 @@
/// Image: Image URL
/// Returns:
/// Local: true if the image is local, false if it is remote
/// Resource: Hugo image resource object, not nil if the image is local
/// Resource: Hugo image resource object
/// Permalink: Image URL
/// Height: Image height
/// Width: Image width
@@ -13,10 +12,15 @@
{{ if .Image }}
{{ $url := urls.Parse .Image }}
{{ $permalink := .Image }}
{{ $resource := "" }}
{{ if not (in (slice "https" "http") $url.Scheme) }}
{{/* Local image */}}
{{ $resource = .Resources.Get (printf "%s" (.Image | safeURL)) }}
{{ $permalink = $resource.RelPermalink }}
{{ else }}
{{/* Remote image */}}
{{ with try (resources.GetRemote $url) }}
{{ with .Err }}
{{ errorf "%s" . }}
@@ -28,25 +32,22 @@
{{ end }}
{{ end }}
{{ if $resource }}
{{- $isSVG := eq $resource.MediaType.SubType "svg" -}}
{{- if $isSVG -}}
{{ with $resource }}
{{ if reflect.IsImageResourceWithMeta . }}
{{ $result = dict
"Local" true
"Resource" $resource
"Permalink" $resource.RelPermalink
"Height" nil
"Width" nil
}}
{{- else -}}
{{- $result = dict
"Local" true
"Resource" $resource
"Permalink" $resource.RelPermalink
"Permalink" $permalink
"Height" $resource.Height
"Width" $resource.Width
}}
{{- end -}}
{{ else }}
{{ $result = dict
"Resource" $resource
"Permalink" $permalink
"Height" nil
"Width" nil
}}
{{ end }}
{{ end }}
{{ end }}
@@ -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") -}}
{{- $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 -}}>
@@ -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 -}}>
+1 -1
View File
@@ -20,7 +20,7 @@ features = [
"search",
]
min_version = "0.154.0"
min_version = "0.157.0"
[author]
name = "Jimmy Cai"