-
-
Save cpbotha/deb310eed14308fe26f7b7d0fabeb34d to your computer and use it in GitHub Desktop.
| {{/* | |
| figure with auto-resizing and srcset v2024-11-24 | |
| Drop-in replacement for Hugo's figure shortcode which uses img srcset | |
| to enable browsers to download only the resolution that they need. | |
| The resizing and srcset magic only works for images that are part of the page | |
| bundle. It will fall back to stock Hugo figure behaviour otherwise. | |
| Improvements that were initially out of reach of my Hugo template programming "skills" | |
| but have now been taken care of: | |
| - [x] gracefully handle images that are not in page bundle, i.e. no image processing available | |
| - [x] use a single configurable sizes array, and derive everything from there | |
| See https://cpbotha.net/2020/05/02/drop-in-replacement-for-hugo-figure-shortcode-with-img-srcset-support/ | |
| - original srcset img shortcode from: https://laurakalbag.com/processing-responsive-images-with-hugo/ | |
| - original hugo figure shortcode from: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/shortcodes/figure.html | |
| - no unnecessary resizes and more nudges by Stéfan van der Walt https://mentat.za.net/ | |
| - mashing together and srcset logic fixes by Charl P. Botha https://cpbotha.net/ | |
| Changes: | |
| - 2024-11-24 work-around for bug in golang where it breaks webp images during resizing. Please add png or jpg version of your webp. | |
| - 2020-05-10 fall back to stock Hugo behaviour when no page bundle found | |
| - 2020-05-04 no unnecessary resizes, sizes in array | |
| - 2020-05-02 initial release | |
| */}} | |
| {{/* hugo will resize to all of these sizes that are smaller than your original. configure if you like! */}} | |
| {{ $sizes := (slice "480" "800" "1200" "1500") }} | |
| {{/* get file that matches the filename as specified as src="" in shortcode */}} | |
| {{ $src := .Page.Resources.GetMatch (printf "*%s*" (.Get "src")) }} | |
| {{/* work-around for bug in golang where webp images are degraded during decoding / processing: | |
| https://github.com/golang/go/issues/40173 | |
| in short: use .webp as src, but also add a png or jpg version. This code will link webp files everywhere, | |
| but it will use the jpg/png as the image processing source */}} | |
| {{ $srcIP := $src }} | |
| {{ if and $src (strings.HasSuffix $src.Name ".webp") }} | |
| {{/* use .jpg if that exists, else .png */}} | |
| {{ $srcPNG := .Page.Resources.GetMatch (printf "*%s*" (replace (.Get "src") ".webp" ".png")) }} | |
| {{ $srcJPG := .Page.Resources.GetMatch (printf "*%s*" (replace (.Get "src") ".webp" ".jpg")) }} | |
| {{ if $srcPNG }} | |
| {{ $srcIP = $srcPNG }} | |
| {{ else if $srcJPG }} | |
| {{ $srcIP = $srcJPG }} | |
| {{ end }} | |
| {{ end }} | |
| <figure{{ with .Get "class" }} class="{{ . }}"{{ end }}> | |
| {{- if .Get "link" -}} | |
| <a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}> | |
| {{- end }} | |
| <img | |
| {{ if $src }} | |
| sizes="(min-width: 35em) 1200px, 100vw" | |
| {{/* only srcset images smaller than or equal to the src (original) image size, as Hugo will upscale small images */}} | |
| srcset=' | |
| {{ range $sizes }} | |
| {{/* explicit MediaType.SubType to support our webp work-around without affecting non-webp use */}} | |
| {{ if ge $src.Width . }}{{ ($srcIP.Resize (printf "%sx %s" . $src.MediaType.SubType)).Permalink }} {{ (printf "%sw" .) }},{{ end }} | |
| {{ end }}' | |
| {{/* when no support for srcset (old browsers, RSS), we load small (800px) */}} | |
| {{/* if image smaller than 800, then load the image itself */}} | |
| {{ if ge $src.Width "800" }}src="{{ ($srcIP.Resize (printf "800x %s" $src.MediaType.SubType)).Permalink }}" | |
| {{ else }}src="{{ $src.Permalink }}" | |
| {{ end }} | |
| {{ else }} | |
| {{/* fall back to stock hugo behaviour when image is not available in bundle */}} | |
| src="{{ .Get "src" }}" | |
| {{ end }} | |
| {{- if or (.Get "alt") (.Get "caption") }} | |
| alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}" | |
| {{- end -}} | |
| {{- with .Get "width" }} width="{{ . }}"{{ end -}} | |
| {{- with .Get "height" }} height="{{ . }}"{{ end -}} | |
| /> <!-- Closing img tag --> | |
| {{- if .Get "link" }}</a>{{ end -}} | |
| {{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}} | |
| <figcaption> | |
| {{ with (.Get "title") -}} | |
| <h4>{{ . }}</h4> | |
| {{- end -}} | |
| {{- if or (.Get "caption") (.Get "attr") -}}<p> | |
| {{- .Get "caption" | markdownify -}} | |
| {{- with .Get "attrlink" }} | |
| <a href="{{ . }}"> | |
| {{- end -}} | |
| {{- .Get "attr" | markdownify -}} | |
| {{- if .Get "attrlink" }}</a>{{ end }}</p> | |
| {{- end }} | |
| </figcaption> | |
| {{- end }} | |
| </figure> |
Very cool.
I've made some changes to handle the situation where:
- The image is an SVG
- The image needs to be rotated
I'm not great with git/github so see https://petersmith.org/blog/2023/01/19/figure-shortcode-again-/ for details
If you add the below on line #59 you will get a better pagespeed score with your images. Surprisingly, it only cares if the value is defined. Not that it matches the dimensions of the specific image being served.
width="{{ $src.Width }}"
height="{{ $src.Height }}"
I have just updated the shortcode to support working around this 4-year old webp decoding bug in the golang standard library: golang/go#40173
Here is more discussion on Hugo's github: gohugoio/hugo#8879
In short, the golang stdlib mangles webp images during decoding, which results in visible colour changes. I ran into this in a recent post with screenshots with white backgrounds, which turned into super grey.
With these changes in the shortcode, you can add e.g. "my_image.webp" and use that in the src and link attributes, but you must also add a "my_image.png" (or .png) version. The .png or .jpg will be used for all resizing, but your final page will link only the webp files.
Thanks for the note, and the thanks!
I did just double-check: Fortunately the comment right above that line fortunately describes the intent, and the code does follow that.