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)