Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add constrain media and media fit settings to product page #2052

Merged
merged 11 commits into from
Nov 17, 2022
7 changes: 6 additions & 1 deletion assets/quick-add.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
.quick-add-modal__content {
bottom: 3.2rem;
}

.quick-add-modal__content-info > * {
max-height: 100%;
}
Expand Down Expand Up @@ -209,3 +209,8 @@ quick-add-modal .product-form__buttons {
min-width: 100%;
box-sizing: border-box;
}

quick-add-modal .product-media-container.constrain-height .media {
/* constrain behavior tbd, negate constrain effects on quick add modal for now */
padding-top: var(--ratio-percent);
}
69 changes: 65 additions & 4 deletions assets/section-main-product.css
Original file line number Diff line number Diff line change
Expand Up @@ -1366,14 +1366,79 @@ a.product__text {
/* Product-thumbnail snippet */

.product-media-container {
--aspect-ratio: var(--preview-ratio);
--ratio-percent: calc(1 / var(--aspect-ratio) * 100%);
position: relative;
width: 100%;
max-width: calc(100% - calc(var(--media-border-width) * 2));
}

.product-media-container.constrain-height {
/* amount to subtract from viewport height when calculating media height */
--viewport-offset: 400px;
/* smallest height the image is allowed to scale down to */
--constrained-min-height: 300px;
/* the larger of either the calculated viewport height or the fixed minimum */
--constrained-height: max(var(--constrained-min-height), calc(100vh - var(--viewport-offset)));
margin-right: auto;
margin-left: auto;
}

@media screen and (min-width: 750px) {
.product-media-container {
max-width: 100%;
}

.product-media-container:not(.media-type-image) {
/* override to use actual media ratio (not poster ratio) for video/models on desktop */
--aspect-ratio: var(--ratio);
}

.product-media-container.constrain-height {
--viewport-offset: 170px;
--constrained-min-height: 500px;
}

.product-media-container.media-fit-cover,
.product-media-container.media-fit-cover .product__modal-opener,
.product-media-container.media-fit-cover .media {
/* allow media to vertically fill available grid row space */
height: 100%;
}

.product-media-container.media-fit-cover .deferred-media__poster img {
/* allow deferred posters fill media container */
object-fit: cover;
width: 100%;
}
}

.product-media-container .media {
padding-top: var(--ratio-percent);
}

.product-media-container.constrain-height .media {
/* the smaller of either the constrained height or the full size default */
padding-top: min(var(--constrained-height), var(--ratio-percent));
}

.product-media-container.constrain-height.media-fit-contain {
/* the smaller of either the constrained width or the full size default */
--contained-width: calc(var(--constrained-height) * var(--aspect-ratio));
width: min(var(--contained-width), 100%);
}

@media screen and (max-width: 749px) {
.product-media-container.media-fit-cover {
/* allow media to vertically fill available mobile slide space */
display: flex;
align-self: stretch;
}

.product-media-container.media-fit-cover .media {
/* allow media img element to scale relative to modal-opener/product-media-container */
position: initial;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little nitpick. This could be moved before the media query @media screen and (min-width: 750px) { on line 1387 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this and next comment could be address to tidy up the stylesheet 👍

Copy link
Contributor Author

@kmeleta kmeleta Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, moving that made me want to move other stuff. I struggled to find an order I liked for these things. It should have more of a base > mobile > desktop order to it now. Should be safe enough but wouldn't hurt to give things a quick run through to make sure the reordering didn't expose any specificity traps 7bd9b03


.product-media-container .product__modal-opener {
Expand All @@ -1386,7 +1451,3 @@ a.product__text {
display: none;
}
}

.product-media-container .media {
padding-top: var(--ratio-percent);
}
20 changes: 16 additions & 4 deletions locales/en.default.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2019,17 +2019,20 @@
"label": "Thumbnail carousel"
}
},
"constrain_to_viewport": {
"label": "Constrain media to screen size"
},
"media_size": {
"label": "Desktop media size",
"label": "Desktop media width",
"info": "Media is automatically optimized for mobile.",
"options__1": {
"label": "Small"
"label": "Small (45%)"
},
"options__2": {
"label": "Medium"
"label": "Medium (55%)"
},
"options__3": {
"label": "Large"
"label": "Large (65%)"
}
},
"image_zoom": {
Expand All @@ -2055,6 +2058,15 @@
"label": "Right"
}
},
"media_fit": {
"label": "Media fit",
"options__1": {
"label": "Original"
},
"options__2": {
"label": "Fill"
}
},
"mobile_thumbnails": {
"label": "Mobile layout",
"options__1": {
Expand Down
62 changes: 42 additions & 20 deletions sections/main-product.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -2074,20 +2074,46 @@
},
{
"type": "select",
"id": "media_position",
"id": "media_size",
"options": [
{
"value": "left",
"label": "t:sections.main-product.settings.media_position.options__1.label"
"value": "small",
"label": "t:sections.main-product.settings.media_size.options__1.label"
},
{
"value": "right",
"label": "t:sections.main-product.settings.media_position.options__2.label"
"value": "medium",
"label": "t:sections.main-product.settings.media_size.options__2.label"
},
{
"value": "large",
"label": "t:sections.main-product.settings.media_size.options__3.label"
}
],
"default": "left",
"label": "t:sections.main-product.settings.media_position.label",
"info": "t:sections.main-product.settings.media_position.info"
"default": "large",
"label": "t:sections.main-product.settings.media_size.label",
"info": "t:sections.main-product.settings.media_size.info"
},
{
"type": "checkbox",
"id": "constrain_to_viewport",
"default": false,
"label": "t:sections.main-product.settings.constrain_to_viewport.label"
},
{
"type": "select",
"id": "media_fit",
"options": [
{
"value": "contain",
"label": "t:sections.main-product.settings.media_fit.options__1.label"
},
{
"value": "cover",
"label": "t:sections.main-product.settings.media_fit.options__2.label"
}
],
"default": "contain",
"label": "t:sections.main-product.settings.media_fit.label"
},
{
"type": "select",
Expand Down Expand Up @@ -2115,24 +2141,20 @@
},
{
"type": "select",
"id": "media_size",
"id": "media_position",
"options": [
{
"value": "small",
"label": "t:sections.main-product.settings.media_size.options__1.label"
},
{
"value": "medium",
"label": "t:sections.main-product.settings.media_size.options__2.label"
"value": "left",
"label": "t:sections.main-product.settings.media_position.options__1.label"
},
{
"value": "large",
"label": "t:sections.main-product.settings.media_size.options__3.label"
"value": "right",
"label": "t:sections.main-product.settings.media_position.options__2.label"
}
],
"default": "large",
"label": "t:sections.main-product.settings.media_size.label",
"info": "t:sections.main-product.settings.media_size.info"
"default": "left",
"label": "t:sections.main-product.settings.media_position.label",
"info": "t:sections.main-product.settings.media_position.info"
},
{
"type": "select",
Expand Down
30 changes: 28 additions & 2 deletions snippets/product-media-gallery.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,20 @@
data-media-id="{{ section.id }}-{{ featured_media.id }}"
>
{%- assign media_position = 1 -%}
{% render 'product-thumbnail', media: featured_media, media_count: media_count, position: media_position, desktop_layout: section.settings.gallery_layout, mobile_layout: section.settings.mobile_thumbnails, loop: section.settings.enable_video_looping, modal_id: section.id, xr_button: true, media_width: media_width, lazy_load: false %}
{% render 'product-thumbnail',
media: featured_media,
media_count: media_count,
position: media_position,
desktop_layout: section.settings.gallery_layout,
mobile_layout: section.settings.mobile_thumbnails,
loop: section.settings.enable_video_looping,
modal_id: section.id,
xr_button: true,
media_width: media_width,
media_fit: section.settings.media_fit,
constrain_to_viewport: section.settings.constrain_to_viewport,
lazy_load: false
%}
</li>
{%- endif -%}
{%- for media in product.media -%}
Expand All @@ -88,8 +101,21 @@
if media_position > 1
assign lazy_load = true
endif
render 'product-thumbnail', media: media, media_count: media_count, position: media_position, desktop_layout: section.settings.gallery_layout, mobile_layout: section.settings.mobile_thumbnails, loop: section.settings.enable_video_looping, modal_id: section.id, xr_button: true, media_width: media_width, lazy_load: lazy_load
-%}
{% render 'product-thumbnail',
media: media,
media_count: media_count,
position: media_position,
desktop_layout: section.settings.gallery_layout,
mobile_layout: section.settings.mobile_thumbnails,
loop: section.settings.enable_video_looping,
modal_id: section.id,
xr_button: true,
media_width: media_width,
media_fit: section.settings.media_fit,
constrain_to_viewport: section.settings.constrain_to_viewport,
lazy_load: lazy_load
%}
</li>
{%- endunless -%}
{%- endfor -%}
Expand Down
13 changes: 6 additions & 7 deletions snippets/product-thumbnail.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- loop: {Boolean} Enable video looping (optional)
- modal_id: {String} The product modal that will be shown by clicking the thumbnail
- xr_button: {Boolean} Renders the "View in your space" button (shopify-xr-button) if the media is a 3D Model
- constrain_to_viewport: {Boolean} Force media height to fit within viewport
- media_fit: {String} Method ("contain" or "cover") to fit image into container
- media_width: {Float} The width percentage that the media column occupies on desktop.
- lazy_load: {Boolean} Image should be lazy loaded. Default: true (optional)

Expand Down Expand Up @@ -38,9 +40,6 @@
assign mobile_columns = 2
endif

assign preview_media_ratio = 1 | divided_by: media.preview_image.aspect_ratio | times: 100
assign media_ratio = 1 | divided_by: media.aspect_ratio | times: 100

if media.media_type == 'image'
assign image_class = 'image-magnify-' | append: section.settings.image_zoom
endif
Expand All @@ -51,8 +50,8 @@
{%- endcapture -%}

<div
class="product-media-container gradient global-media-settings"
style="--ratio-percent: {{ preview_media_ratio }}%;"
class="product-media-container media-type-{{ media.media_type }} media-fit-{{ media_fit }}{% if constrain_to_viewport %} constrain-height{% endif %} gradient global-media-settings"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to prefer having conditional classes at the end rather than in the middle. I find the whole thing easier to read.

Like this:

Suggested change
class="product-media-container media-type-{{ media.media_type }} media-fit-{{ media_fit }}{% if constrain_to_viewport %} constrain-height{% endif %} gradient global-media-settings"
class="product-media-container media-type-{{ media.media_type }} media-fit-{{ media_fit }} gradient global-media-settings{% if constrain_to_viewport %} constrain-height{% endif %}"

Any thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 :D

Copy link
Contributor Author

@kmeleta kmeleta Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never aligned on this formally as a team. I don't feel too strongly about pushing back in this particular case, so I've made the change but I'm not always sold on basing the order of classes on the arbitrary decision to include an inline conditional or not. gradient for a example is a much more generic global class and we usually like to order those after more specific classes. Again it's a bit of a toss up here if gradient and media-settings are more generic than constrain-height, but that's how I think about it.

class=”grid grid--2-col has-border show-heading-or-something slider {% if show_slider_desktop %} slider–desktop{% endif %}{% if some_setting %} grid--1-col{% endif %}”

Also consider this ^ example, grid--1-col gets moved to the end and is now harder to associate with other relevant related classes. I don't like that and that doesn't make this code any easier to understand in the editor or in the browser inspector

style="--ratio: {{ media.aspect_ratio | default: 1.0 }}; --preview-ratio: {{ media.preview_image.aspect_ratio | default: 1.0 }};"
>
<noscript>
{%- if media.media_type == 'video' or media.media_type == 'external_video' -%}
Expand Down Expand Up @@ -111,9 +110,9 @@

{%- if media.media_type != 'image' -%}
{%- if media.media_type == 'model' -%}
<product-model class="deferred-media media media--transparent no-js-hidden" style="--ratio-percent: 100%;" data-media-id="{{ media.id }}">
<product-model class="deferred-media media media--transparent no-js-hidden" data-media-id="{{ media.id }}">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, but something I'd like to bring up regarding previous context from product media just to ensure we log this as a future decision in case we're overriding a past UX decision.

I remember we decided back in 2019 when 3D models were introduced to always display them in 1:1 aspect ratio. The rationale was that Shopify automatically generates a square poster image for uploaded 3D models, so this would ensure a smooth transition between the poster and the model's initial position. With the changes on this PR, 3D models can now be seen in any aspect ratio when the Media fit: Fill is enabled. This leads to a less smooth transition as you can see in the video below.

08-19-7jpi3-8bdmc.mp4

Because this is a setting and we're not changing the default behaviour, I think this is probably fine. If we were to force 3D models to be square would add a layer of "magic" to the setting and make it less clear.

I could not find the documentation to support this "always use square" principle, though, so I will cc @melissaperreault to see if she has any context from that time that could be relevant.

Copy link
Contributor Author

@kmeleta kmeleta Nov 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default we preserve the existing 1:1 ratios for models, but you are correct, when using media fit: fill there are scenarios we they can diverge from that. I believe these cases a fairly minimal as I understand, but I'll outline them below for clarity. Let me know if I'm missing any.

  1. In cases of media_fit: fill and desktop_layout: 2 columns and model shares a row with a portrait image. If the other image on the same row is a landscape or also square, the model media shouldn't crop. Particularly if 1:1 is the most common product image ratio, this should be an infrequent combination.

  1. In cases of media_fit: fill and constrain: true and viewport is small enough to constrain even square images. This can happen on tablet/desktop whenever models render fullwidth in the media column. I don't think this feel this will be super common either, but I do I think this is more likely to occur than the other scenario, especially if we were to default the constrain setting to true/on.

If we were to force 3D models to be square would add a layer of "magic" to the setting and make it less clear

Just want to mention this is an option of course, particularly for cases when the models are fullwidth (anything but desktop layout: 2 columns), but it would indeed be an unpredictable exception to the setting, which as I understand is our main issue with it. I also don't love the idea of doing this for the 2 column layout anyway just because it would create the vertical whitespace that the media fit setting specifically provides an option to prevent.

{%- else -%}
<deferred-media class="deferred-media media media--transparent no-js-hidden" style="--ratio-percent: {{ media_ratio }}%;" data-media-id="{{ media.id }}">
<deferred-media class="deferred-media media media--transparent no-js-hidden" data-media-id="{{ media.id }}">
{%- endif -%}
<button id="Deferred-Poster-Modal-{{ media.id }}" class="deferred-media__poster" type="button">
<span class="deferred-media__poster-button motion-reduce">
Expand Down