I created image components a couple of times before:
But browsers keep improving - now almost all modern browsers support:
I checked existing solutions and they either don't do what I want or are complicated (to my taste):
- Basic
- sets HTML attribute
src
(required) - sets HTML attributes
alt
, andclass
if they passed
- sets HTML attribute
- Lazy-load
- sets HTML attributes
loading="lazy"
,decoding="async"
- sets HTML attributes
width
,height
to prevent reflow (LCP)- including for SVG
- sets HTML attributes
- Responsive
- sets HTML attribute
srcset
and resizes the image- but doesn't upscale image
- adds
source
element forwebp
format
- sets HTML attribute
- Modern LQIP
- Can be disabled for transparent images (png, webp)
- Another option would be dominant color (not implemented)
- Portable markdown links
- Dark mode (not implemented)
- Maybe URL params for resize params (not implemented)
There are different ways to implement image components in Hugo:
I think the most pragmatic way would be to implement partial which accepts an image as a resource. This way it can be reused in render hooks and shortcodes, and the resolution of the image would be the responsibility of the caller.
It can look something like this:
{{ partial "picture.html" (dict "img" $img "alt" $.Text) }}
Partial picture.html
would be responsible for detecting width and height, resizing the image, and rendering HTML. There are no special requirements for HTML or CSS so it would be a pretty portable solution (just copy one file).
Partial sets width and height for the image, but this is done so that the browser would know the ratio and can calculate the area that the image would take. Probably you want to use CSS to set the desired size for the image, for example:
img {
max-width: 100%;
height: auto;
}
For LQIP to work picture
needs to be the same size as the image. There can be different solutions, but one of the simplest is to make a picture block element - picture { display: block; }
Also possible to add blur to the background which would improve appearance:
picture > img {
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
Note: LQIP doesn't work in Firefox because of this bug
I'm not sure yet how to implement "dark mode" for images. From an HTML point of view it is trivial to implement:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="..." />
<source media="(prefers-color-scheme: light)" srcset="..." />
<img src="..." />
</picture>
But how to do it from Markdown's point of view? Some options are:
- convention. If there is an image
img.png
andimg_dark.png
, the system can automatically find the second image and use it for dark mode - shortcode. Will work, but this is not a markdown solution
- Use some kind of marker in the url to distinguish dark-/light-mode images. For example,
#gh-dark-mode-only
or#gh-light-mode-only
. See Specifying the theme an image is shown to - SVGs don't need a special solution, they can handle it internally. See Creating SVG that appears black in light mode and light in dark mode
- for black and white images
filter: invert(1)
would do the trick