
Written July 17, 2025. Tagged Shopify.
I've just set up a Shopify store for my partner using Shopify's own Craft theme.
When clicking a product image to zoom/enlarge it, Craft can show it larger than its actual ("intrinsic") size, making it blurry:
So I fixed it. Now:
I keep a local copy of theme changes. This is the diff:
diff --git craft-theme/assets/section-main-product.css craft-theme/assets/section-main-product.css
index c9dfc58..176379e 100644
--- craft-theme/assets/section-main-product.css
+++ craft-theme/assets/section-main-product.css
@@ -706,13 +706,17 @@ a.product__text {
@media screen and (min-width: 750px) {
.product-media-modal__content {
- padding: 2rem 11rem;
+ padding: 1rem;
}
.product-media-modal__content > * {
width: 100%;
}
+ .product-media-modal__content > img {
+ max-width: fit-content;
+ }
+
.product-media-modal__content > * + * {
margin-top: 2rem;
}
@@ -724,10 +728,6 @@ a.product__text {
}
@media screen and (min-width: 990px) {
- .product-media-modal__content {
- padding: 2rem 11rem;
- }
-
.product-media-modal__content > * + * {
margin-top: 1.5rem;
}
diff --git craft-theme/snippets/product-media.liquid craft-theme/snippets/product-media.liquid
index 0d6cb41..bc1becb 100644
--- craft-theme/snippets/product-media.liquid
+++ craft-theme/snippets/product-media.liquid
@@ -15,6 +15,10 @@
{% endcomment %}
{%- if media.media_type == 'image' -%}
+ {%- assign outer_max_width_on_small_displays = 1100 -%}
+ {%- assign outer_max_width_on_large_displays = 4096 -%}
+ {%- assign max_width_on_small_displays = media.preview_image.width | at_most: outer_max_width_on_small_displays -%}
+ {%- assign max_width_on_large_displays = media.preview_image.width | at_most: outer_max_width_on_large_displays -%}
<img
class="global-media-settings global-media-settings--no-shadow{% if variant_image %} product__media-item--variant{% endif %}"
srcset="
@@ -28,12 +32,12 @@
{%- if media.preview_image.width >= 4096 -%}{{ media.preview_image | image_url: width: 4096 }} 4096w,{%- endif -%}
{{ media.preview_image | image_url }} {{ media.preview_image.width }}w
"
- sizes="(min-width: 750px) calc(100vw - 22rem), 1100px"
- src="{{ media.preview_image | image_url: width: 1445 }}"
+ sizes="(min-width: 750px) min(calc(100vw - 2rem), {{ max_width_on_large_displays }}px), {{ max_width_on_small_displays }}px"
+ src="{{ media.preview_image | image_url: width: max_width_on_large_displays }}"
alt="{{ media.alt | escape }}"
loading="lazy"
- width="1100"
- height="{{ 1100 | divided_by: media.preview_image.aspect_ratio | ceil }}"
+ width="{{ max_width_on_large_displays }}"
+ height="{{ max_width_on_large_displays | divided_by: media.preview_image.aspect_ratio | ceil }}"
data-media-id="{{ media.id }}"
>
{%- else -%}Much better!
The srcset remains unchanged, meaning the browser will still request an appropriate image size based on viewport size and pixel density (retina displays). The max image it offers up is 4096 pixels wide, which explains that magic number above. (Also see the rationale for similar srcset values in the "Dawn" theme.)
I picked a 1rem padding because it Looked Good™ – the default 11rem is inexplicably large, leaving too little room for the image itself.
The max-width: fit-content defeats a width: 100% that would otherwise stretch the image beyond its intrinsic size.
I have not considered videos or other media types, for now. They remain unchanged, other than the smaller container padding.