Goal: Generate multiple image sizes within <source>
tags using provided values for media queries and image dimensions. Serve them as both the original file format and as wepb
for supporting browsers.
Initial situation:
This is a common pattern I use to create an optimal sized and formatted image, that will be cropped around an editor-defined focus point. However it's super annoying having to write this for each and every component with only the numbers changing from one to another:
<picture class="block w-full" >
<source
media="(min-width: 1280px)"
srcset="{{ glide:image width="1120" height="1120" format="webp" fit="crop_focal" }}"
type="image/webp"
>
<source
media="(min-width: 1280px)"
srcset="{{ glide:image width="1120" height="1120" fit="crop_focal" }}"
type="{{ image.mime_type }}"
>
<source
media="(min-width: 980px)"
srcset="{{ glide:image width="900" height="520" format="webp" fit="crop_focal" }}"
type="image/webp">
<source
media="(min-width: 980px)"
srcset="{{ glide:image width="900" height="520" fit="crop_focal" }}"
type="{{ image.mime_type }}"
>
<source
data-srcset="{{ glide:image width="450" height="260" format="webp" fit="crop_focal" }}"
type="image/webp"
>
<img
class="block object-cover w-full h-auto lazyload"
src="{{ glide:image width="450" height="260" fit="crop_focal" }}"
alt="{{ title }}"
height="520"
width="900"
>
</picture>
💡 Idea
A partial could take the following arguments:
{{ picture :viewports="2000['1600', '800'], 2000['800', '600'], DEFAULT['400', '300']" }}
Creating and passing an array on the fly inside an attribute doesn't work in antlers. It could be passed as a comma separated string and then exploded using the explode modifier.
But wait, I just recently learned that we can use YAML frontmatter right within antlers templates \o/.
It seems way cleaner to define viewports with their respective images sizes in Frontmatter, which can then be passed as an array. No need for exploding strings at all:
---
viewports:
2000: [1600, 800]
1000: [800, 600]
'DEFAULT': [400, 300]
---
{{ partial:components/test :viewports="view:viewports" }}
This can be iterated over with foreach to generate a <source>
tag with the media_query value being used as the value for min-width
and the nested
{{ foreach:viewports as="media_query|sizes" }}
<p>Media Query: {{ media_query }}</p>
<p>Width: {{ sizes:0 }}</p>
<p>Height: {{ sizes:1 }}</p>
{{ /foreach:viewports }}
Better Solution
YAML's nested object syntax makes this look a bit nicer:
---
viewports:
2000: {'w': 1600, 'h': 800}
1000: {'w': 800, 'h': 600}
'DEFAULT': {'w': 400, 'h': 300}
---
{{# … #}}
{{ image }}
{{ partial:components/picture_cropped
:viewports="view:viewports"
:image="image"
lazy="true"
}}
{{ /image }}
This can be iterated over nicely with {{ foreach:viewports as "media_query|dimensions"}}
:
{{ if image }}
<picture>
{{ asset :url="image" }}
{{ if extension == 'svg' || extension == 'gif' }}
<img
class="{{ class }}"
src="{{ url }}"
alt="{{ alt }}"
/>
{{ else }}
{{ foreach:viewports as="media_query|dimensions" }}
{{ if media_query != 'DEFAULT'}}
<source
media="(min-width: {{ media_query }}px)"
srcset="{{ glide:image :width="dimensions:w" :height="dimensions:h" format="webp" fit="crop_focal" }}"
type="image/webp"
/>
<source
media="(min-width: {{ media_query }})px"
srcset="{{ glide:image :width="dimensions:w" :height="dimensions:h" fit="crop_focal" }}"
type="{{ image.mime_type }}"
/>
{{ else }}
<source
srcset="{{ glide:image :width="dimensions:w" :height="dimensions:h" format="webp" fit="crop_focal" }}"
type="image/webp"
/>
<img
class="block object-cover w-full h-auto {{ class }}"
src="{{ glide:image :width="dimensions:w" :height="dimensions:h" fit="crop_focal" }}"
alt="{{ alt }}"
height="{{ dimensions:h }}"
width="{{ dimensions:w }}"
{{ if lazy }}
loading="lazy"
{{ /if }}
/>
{{ /if }}
{{ /foreach:viewports }}
{{ /if }}
{{ /asset }}
</picture>
{{ /if }}
Top comments (0)