Dawn theme just had an update on December 2021, and added thumbnail sliders to desktop. I want to make it personalized and did a vertical slider instead with zoom feature. I tried to make the instructions simple and easy to follow.
IOS friendly (",)
NOTE: If you have version Dawn 2.5 and lower, please refer on this link.
To start:
1. We have to go to our code editor. Shopify admin store > Themes > Edit code.
2. Look for the main-product.liquid under the Sections folder and replace the code below.
{% comment %}theme-check-disable TemplateLength{% endcomment %} {{ 'section-main-product.css' | asset_url | stylesheet_tag }} {{ 'component-accordion.css' | asset_url | stylesheet_tag }} {{ 'component-price.css' | asset_url | stylesheet_tag }} {{ 'component-rte.css' | asset_url | stylesheet_tag }} {{ 'component-slider.css' | asset_url | stylesheet_tag }} {{ 'component-rating.css' | asset_url | stylesheet_tag }} {{ 'component-loading-overlay.css' | asset_url | stylesheet_tag }} {{ 'component-deferred-media.css' | asset_url | stylesheet_tag }} {%- style -%} .section-{{ section.id }}-padding { padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px; padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px; } @media screen and (min-width: 750px) { .section-{{ section.id }}-padding { padding-top: {{ section.settings.padding_top }}px; padding-bottom: {{ section.settings.padding_bottom }}px; } } {%- endstyle -%} <script src="{{ 'product-form.js' | asset_url }}" defer="defer"></script> {%- assign first_3d_model = product.media | where: "media_type", "model" | first -%} {%- if first_3d_model -%} {{ 'component-product-model.css' | asset_url | stylesheet_tag }} <link id="ModelViewerStyle" rel="stylesheet" href="https://cdn.shopify.com/shopifycloud/model-viewer-ui/assets/v1.0/model-viewer-ui.css" media="print" onload="this.media='all'"> <link id="ModelViewerOverride" rel="stylesheet" href="{{ 'component-model-viewer-ui.css' | asset_url }}" media="print" onload="this.media='all'"> {%- endif -%} <section class="page-width section-{{ section.id }}-padding" style="--btn-color: {{section.settings.button_color}};"> <div class="product product--{{ section.settings.media_size }} product--thumbnail_slider grid grid--1-col {% if product.media.size > 0 %}grid--2-col-tablet{% else %}product--no-media{% endif %}"> <div class="grid__item product__media-wrapper"> <media-gallery id="MediaGallery-{{ section.id }}" role="region" {% if section.settings.enable_sticky_info %}class="product__media-gallery"{% endif %} aria-label="{{ 'products.product.media.gallery_viewer' | t }}"> <div id="GalleryStatus-{{ section.id }}" class="visually-hidden" role="status"></div> <main-slider-component id="GalleryViewer-{{ section.id }}" class="slider-mobile-gutter"> <a class="skip-to-content-link button visually-hidden" href="#ProductInfo-{{ section.id }}"> {{ "accessibility.skip_to_product_info" | t }} </a> {%- liquid assign variant_images = product.images | where: 'attached_to_variant?', true | map: 'src' assign media_count = product.media.size if section.settings.hide_variants assign media_count = media_count | minus: variant_images.size | plus: 1 endif if section.settings.media_size == 'large' assign media_width = 0.65 elsif section.settings.media_size == 'medium' assign media_width = 0.55 elsif section.settings.media_size == 'small' assign media_width = 0.45 endif -%} <button type="button" class="slider-button main-button--prev button-icon {% if media_count <= 2 %} hidden {% endif %} {% if section.settings.hide_buttons %} mobile-hide{% endif %}" name="previous" aria-label="{{ 'general.slider.previous_slide' | t }}"> <svg class="icon" viewBox="0 0 15 15"> <path class="st0" d="M1.17,0.78l12.97,6.05c0.14,0.07,0.15,0.26,0.01,0.33L1.08,14.21c-0.2,0.11-0.4-0.13-0.26-0.29l4.46-6.78 c0.07-0.08,0.06-0.19-0.01-0.26l-4.34-5.8C0.77,0.94,0.96,0.69,1.17,0.78z"/> </svg> </button> <ul id="Slider-Gallery-{{ section.id }}" class="product__media-list grid grid--peek list-unstyled slider slider--mobile" role="list"> {%- if product.selected_or_first_available_variant.featured_media != null -%} {%- assign featured_media = product.selected_or_first_available_variant.featured_media -%} <li id="Slide-{{ section.id }}-{{ featured_media.id }}" class="product__media-item grid__item slider__slide is-active{% if featured_media.media_type != 'image' %} product__media-item--full{% endif %}{% if section.settings.hide_variants and variant_images contains featured_media.src %} product__media-item--variant{% endif %}" data-media-id="{{ section.id }}-{{ featured_media.id }}"> {%- assign media_position = 1 -%} {% render 'product-thumbnail', media: featured_media, position: media_position, loop: section.settings.enable_video_looping, modal_id: section.id, xr_button: true, media_width: media_width %} </li> {%- endif -%} {%- for media in product.media -%} {%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%} <li id="Slide-{{ section.id }}-{{ media.id }}" class="product__media-item grid__item slider__slide{% if product.selected_or_first_available_variant.featured_media == null and forloop.index == 1 %} is-active{% endif %}{% if media.media_type != 'image' %} product__media-item--full{% endif %}{% if section.settings.hide_variants and variant_images contains media.src %} product__media-item--variant{% endif %}" data-media-id="{{ section.id }}-{{ media.id }}"> {%- assign media_position = media_position | default: 0 | plus: 1 -%} {% render 'product-thumbnail', media: media, position: media_position, loop: section.settings.enable_video_looping, modal_id: section.id, xr_button: true, media_width: media_width, lazy_load: true %} </li> {%- endunless -%} {%- endfor -%} </ul> <button type="button" class="slider-button main-button--next button-icon {% if media_count <= 2 %} hidden{% endif %} {% if section.settings.hide_buttons %} mobile-hide{% endif %}" name="next" aria-label="{{ 'general.slider.next_slide' | t }}"> <svg class="icon" viewBox="0 0 15 15"> <path class="st0" d="M1.17,0.78l12.97,6.05c0.14,0.07,0.15,0.26,0.01,0.33L1.08,14.21c-0.2,0.11-0.4-0.13-0.26-0.29l4.46-6.78 c0.07-0.08,0.06-0.19-0.01-0.26l-4.34-5.8C0.77,0.94,0.96,0.69,1.17,0.78z"/> </svg> </button> <div class="slider-buttons no-js-hidden{% if media_count < 2 or section.settings.mobile_thumbnails == 'show' %} small-hide{% endif %}"> <div class="slider-counter caption"> <span class="slider-counter--current">1</span> <span aria-hidden="true"> / </span> <span class="visually-hidden">{{ 'general.slider.of' | t }}</span> <span class="slider-counter--total">{{ media_count }}</span> </div> </div> </main-slider-component> {%- if first_3d_model -%} <button class="button button--full-width product__xr-button" type="button" aria-label="{{ 'products.product.xr_button_label' | t }}" data-shopify-xr data-shopify-model3d-id="{{ first_3d_model.id }}" data-shopify-title="{{ product.title | escape }}" data-shopify-xr-hidden > {% render 'icon-3d-model' %} {{ 'products.product.xr_button' | t }} </button> {%- endif -%} {%- if media_count > 1 -%} <vertical-slider-component hidden data-mobile="{% if section.settings.mobile_thumbnails == 'show' %}true {% else %}false{% endif %}" data-slider-id="GalleryThumbnails-{{ section.id }}" id="GalleryThumbnails-{{ section.id }}" class="{% if section.settings.mobile_thumbnails == 'show' %}vertical-hide {% endif %}thumbnail-slider slider-mobile-gutter{% if section.settings.mobile_thumbnails == 'hide' %} small-hide{% endif %}{% if media_count <= 3 %} thumbnail-slider--no-slide{% endif %}" data-media-count="{{media_count}}"> <button type="button" class="button-icon slider-button slider-button--prev {% if media_count <= 5 %} medium-hide large-up-hide{% endif %}" name="previous" aria-label="{{ 'general.slider.previous_slide' | t }}" aria-controls="GalleryThumbnails-{{ section.id }}" data-step="5"> <svg class="icon" viewBox="0 0 15 15"> <path class="st0" d="M1.17,0.78l12.97,6.05c0.14,0.07,0.15,0.26,0.01,0.33L1.08,14.21c-0.2,0.11-0.4-0.13-0.26-0.29l4.46-6.78 c0.07-0.08,0.06-0.19-0.01-0.26l-4.34-5.8C0.77,0.94,0.96,0.69,1.17,0.78z"/> </svg> </button> <ul id="Slider-Thumbnails-{{ section.id }}" class="thumbnail-list list-unstyled slider slider--mobile slider--tablet-up"> {%- if featured_media != null -%} {%- capture media_index -%} {%- if featured_media.media_type == 'model' -%}{%- increment model_index -%} {%- elsif featured_media.media_type == 'video' or featured_media.media_type == 'external_video' -%}{%- increment video_index -%} {%- elsif featured_media.media_type == 'image'%}{%- increment image_index -%} {%- endif -%} {%- endcapture -%} {%- assign media_index = media_index | plus: 1 -%} <li id="Slide-Thumbnails-{{ section.id }}-0" class="thumbnail-list__item slider__slide{% if section.settings.hide_variants and variant_images contains featured_media.src %} thumbnail-list_item--variant{% endif %}" data-target="{{ section.id }}-{{ featured_media.id }}" data-media-position="{{ media_index }}"> <button class="thumbnail global-media-settings global-media-settings--no-shadow {% if featured_media.preview_image.aspect_ratio > 1 %}thumbnail--wide{% else %}thumbnail--narrow{% endif %}" aria-label="{%- if featured_media.media_type == 'image' -%}{{ 'products.product.media.load_image' | t: index: media_index }}{%- elsif featured_media.media_type == 'model' -%}{{ 'products.product.media.load_model' | t: index: media_index }}{%- elsif featured_media.media_type == 'video' or featured_media.media_type == 'external_video' -%}{{ 'products.product.media.load_video' | t: index: media_index }}{%- endif -%}" aria-current="true" aria-controls="GalleryViewer-{{ section.id }}" aria-describedby="Thumbnail-{{ section.id }}-0"> <img id="Thumbnail-{{ section.id }}-0" srcset="{% if featured_media.preview_image.width >= 54 %}{{ featured_media.preview_image | image_url: width: 54 }} 54w,{% endif %} {% if featured_media.preview_image.width >= 74 %}{{ featured_media.preview_image | image_url: width: 74 }} 74w,{% endif %} {% if featured_media.preview_image.width >= 104 %}{{ featured_media.preview_image | image_url: width: 104 }} 104w,{% endif %} {% if featured_media.preview_image.width >= 162 %}{{ featured_media.preview_image | image_url: width: 162 }} 162w,{% endif %} {% if featured_media.preview_image.width >= 208 %}{{ featured_media.preview_image | image_url: width: 208 }} 208w,{% endif %} {% if featured_media.preview_image.width >= 324 %}{{ featured_media.preview_image | image_url: width: 324 }} 324w,{% endif %} {% if featured_media.preview_image.width >= 416 %}{{ featured_media.preview_image | image_url: width: 416 }} 416w,{% endif %}, {{ featured_media.preview_image | image_url }} {{ media.preview_image.width }}w" src="{{ featured_media | image_url: width: 416 }}" sizes="(min-width: {{ settings.page_width }}px) calc(({{ settings.page_width | minus: 100 | times: media_width | round }} - 4rem) / 4), (min-width: 990px) calc(({{ media_width | times: 100 }}vw - 4rem) / 4), (min-width: 750px) calc((100vw - 15rem) / 8), calc((100vw - 14rem) / 3)" alt="{{ featured_media.alt | escape }}" height="208" width="208" loading="lazy" > </button> </li> {%- endif -%} {%- for media in product.media -%} {%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%} {%- capture media_index -%} {%- if media.media_type == 'model' -%}{%- increment model_index -%} {%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%}{%- increment video_index -%} {%- elsif media.media_type == 'image'%}{%- increment image_index -%} {%- endif -%} {%- endcapture -%} {%- assign media_index = media_index | plus: 1 -%} <li id="Slide-Thumbnails-{{ section.id }}-{{ forloop.index }}" class="thumbnail-list__item slider__slide{% if section.settings.hide_variants and variant_images contains media.src %} thumbnail-list_item--variant{% endif %}" data-target="{{ section.id }}-{{ media.id }}" data-media-position="{{ media_index }}"> {%- if media.media_type == 'model' -%} <span class="thumbnail__badge" aria-hidden="true"> {%- render 'icon-3d-model' -%} </span> {%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%} <span class="thumbnail__badge" aria-hidden="true"> {%- render 'icon-play' -%} </span> {%- endif -%} <button class="thumbnail global-media-settings global-media-settings--no-shadow {% if media.preview_image.aspect_ratio > 1 %}thumbnail--wide{% else %}thumbnail--narrow{% endif %}" aria-label="{%- if media.media_type == 'image' -%}{{ 'products.product.media.load_image' | t: index: media_index }}{%- elsif media.media_type == 'model' -%}{{ 'products.product.media.load_model' | t: index: media_index }}{%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%}{{ 'products.product.media.load_video' | t: index: media_index }}{%- endif -%}" {% if media == product.selected_or_first_available_variant.featured_media or product.selected_or_first_available_variant.featured_media == null and forloop.index == 1 %} aria-current="true"{% endif %} aria-controls="GalleryViewer-{{ section.id }}" aria-describedby="Thumbnail-{{ section.id }}-{{ forloop.index }}" > <img id="Thumbnail-{{ section.id }}-{{ forloop.index }}" srcset="{% if media.preview_image.width >= 59 %}{{ media.preview_image | image_url: width: 59 }} 59x,{% endif %} {% if media.preview_image.width >= 118 %}{{ media.preview_image | image_url: width: 118 }} 118w,{% endif %} {% if media.preview_image.width >= 84 %}{{ media.preview_image | image_url: width: 84 }} 84w,{% endif %} {% if media.preview_image.width >= 168 %}{{ media.preview_image | image_url: width: 168 }} 168w,{% endif %} {% if media.preview_image.width >= 130 %}{{ media.preview_image | image_url: width: 130 }} 130w,{% endif %} {% if media.preview_image.width >= 260 %}{{ media.preview_image | image_url: width: 260 }} 260w{% endif %}" src="{{ media | image_url: width: 84, height: 84 }}" sizes="(min-width: 1200px) calc((1200px - 19.5rem) / 12), (min-width: 750px) calc((100vw - 16.5rem) / 8), calc((100vw - 8rem) / 5)" alt="{{ media.alt | escape }}" height="200" width="200" loading="lazy" > </button> </li> {%- endunless -%} {%- endfor -%} </ul> <button type="button" class="button-icon slider-button slider-button--next {% if media_count <= 5 %} medium-hide large-up-hide{% endif %}" name="next" aria-label="{{ 'general.slider.next_slide' | t }}" aria-controls="GalleryThumbnails-{{ section.id }}" data-step="5"> <svg class="icon" viewBox="0 0 15 15"> <path class="st0" d="M1.17,0.78l12.97,6.05c0.14,0.07,0.15,0.26,0.01,0.33L1.08,14.21c-0.2,0.11-0.4-0.13-0.26-0.29l4.46-6.78 c0.07-0.08,0.06-0.19-0.01-0.26l-4.34-5.8C0.77,0.94,0.96,0.69,1.17,0.78z"/> </svg> </button> </vertical-slider-component> {%- if section.settings.mobile_thumbnails == 'show' -%} <slider-component mobile-slider id="GalleryThumbnails-{{ section.id }}" class="mobile-slider slider-mobile-gutter{% if section.settings.mobile_thumbnails == 'hide' %} small-hide{% endif %}{% if media_count <= 3 %} thumbnail-slider--no-slide{% endif %}"> <button type="button" class="mobile-button slider-button slider-button--prev{% if media_count <= 3 %} small-hide{% endif %}{% if media_count <= 4 %} medium-hide large-up-hide{% endif %}" name="previous" aria-label="{{ 'general.slider.previous_slide' | t }}" aria-controls="GalleryThumbnails-{{ section.id }}" data-step="3"> <svg class="icon" viewBox="0 0 15 15"> <path class="st0" d="M1.17,0.78l12.97,6.05c0.14,0.07,0.15,0.26,0.01,0.33L1.08,14.21c-0.2,0.11-0.4-0.13-0.26-0.29l4.46-6.78 c0.07-0.08,0.06-0.19-0.01-0.26l-4.34-5.8C0.77,0.94,0.96,0.69,1.17,0.78z"/> </svg> </button> <ul id="Slider-Thumbnails-{{ section.id }}" class="thumbnail-list list-unstyled slider slider--mobile{% if section.settings.gallery_layout == 'thumbnail_slider' %} slider--tablet-up{% endif %}"> {%- if featured_media != null -%} {%- capture media_index -%} {%- if featured_media.media_type == 'model' -%}{%- increment model_index -%} {%- elsif featured_media.media_type == 'video' or featured_media.media_type == 'external_video' -%}{%- increment video_index -%} {%- elsif featured_media.media_type == 'image'%}{%- increment image_index -%} {%- endif -%} {%- endcapture -%} {%- assign media_index = media_index | plus: 1 -%} <li id="Slide-Thumbnails-{{ section.id }}-0" class="thumbnail-list__item slider__slide{% if section.settings.hide_variants and variant_images contains featured_media.src %} thumbnail-list_item--variant{% endif %}" data-target="{{ section.id }}-{{ featured_media.id }}"> <button class="thumbnail global-media-settings global-media-settings--no-shadow {% if featured_media.preview_image.aspect_ratio > 1 %}thumbnail--wide{% else %}thumbnail--narrow{% endif %}" aria-label="{%- if featured_media.media_type == 'image' -%}{{ 'products.product.media.load_image' | t: index: media_index }}{%- elsif featured_media.media_type == 'model' -%}{{ 'products.product.media.load_model' | t: index: media_index }}{%- elsif featured_media.media_type == 'video' or featured_media.media_type == 'external_video' -%}{{ 'products.product.media.load_video' | t: index: media_index }}{%- endif -%}" aria-current="true" aria-controls="GalleryViewer-{{ section.id }}" aria-describedby="Thumbnail-{{ section.id }}-0" > <img id="Thumbnail-{{ section.id }}-0" srcset="{% if featured_media.preview_image.width >= 54 %}{{ featured_media.preview_image | image_url: width: 54 }} 54w,{% endif %} {% if featured_media.preview_image.width >= 74 %}{{ featured_media.preview_image | image_url: width: 74 }} 74w,{% endif %} {% if featured_media.preview_image.width >= 104 %}{{ featured_media.preview_image | image_url: width: 104 }} 104w,{% endif %} {% if featured_media.preview_image.width >= 162 %}{{ featured_media.preview_image | image_url: width: 162 }} 162w,{% endif %} {% if featured_media.preview_image.width >= 208 %}{{ featured_media.preview_image | image_url: width: 208 }} 208w,{% endif %} {% if featured_media.preview_image.width >= 324 %}{{ featured_media.preview_image | image_url: width: 324 }} 324w,{% endif %} {% if featured_media.preview_image.width >= 416 %}{{ featured_media.preview_image | image_url: width: 416 }} 416w,{% endif %}, {{ featured_media.preview_image | image_url }} {{ media.preview_image.width }}w" src="{{ featured_media | image_url: width: 416 }}" sizes="(min-width: {{ settings.page_width }}px) calc(({{ settings.page_width | minus: 100 | times: media_width | round }} - 4rem) / 4), (min-width: 990px) calc(({{ media_width | times: 100 }}vw - 4rem) / 4), (min-width: 750px) calc((100vw - 15rem) / 8), calc((100vw - 14rem) / 3)" alt="{{ featured_media.alt | escape }}" height="208" width="208" loading="lazy" > </button> </li> {%- endif -%} {%- for media in product.media -%} {%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%} {%- capture media_index -%} {%- if media.media_type == 'model' -%}{%- increment model_index -%} {%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%}{%- increment video_index -%} {%- elsif media.media_type == 'image'%}{%- increment image_index -%} {%- endif -%} {%- endcapture -%} {%- assign media_index = media_index | plus: 1 -%} <li id="Slide-Thumbnails-{{ section.id }}-{{ forloop.index }}" class="thumbnail-list__item slider__slide{% if section.settings.hide_variants and variant_images contains media.src %} thumbnail-list_item--variant{% endif %}" data-target="{{ section.id }}-{{ media.id }}"> {%- if media.media_type == 'model' -%} <span class="thumbnail__badge" aria-hidden="true"> {%- render 'icon-3d-model' -%} </span> {%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%} <span class="thumbnail__badge" aria-hidden="true"> {%- render 'icon-play' -%} </span> {%- endif -%} <button class="thumbnail global-media-settings global-media-settings--no-shadow {% if media.preview_image.aspect_ratio > 1 %}thumbnail--wide{% else %}thumbnail--narrow{% endif %}" aria-label="{%- if media.media_type == 'image' -%}{{ 'products.product.media.load_image' | t: index: media_index }}{%- elsif media.media_type == 'model' -%}{{ 'products.product.media.load_model' | t: index: media_index }}{%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%}{{ 'products.product.media.load_video' | t: index: media_index }}{%- endif -%}" {% if media == product.selected_or_first_available_variant.featured_media or product.selected_or_first_available_variant.featured_media == null and forloop.index == 1 %} aria-current="true"{% endif %} aria-controls="GalleryViewer-{{ section.id }}" aria-describedby="Thumbnail-{{ section.id }}-{{ forloop.index }}" > <img id="Thumbnail-{{ section.id }}-{{ forloop.index }}" srcset="{% if media.preview_image.width >= 59 %}{{ media.preview_image | image_url: width: 59 }} 59x,{% endif %} {% if media.preview_image.width >= 118 %}{{ media.preview_image | image_url: width: 118 }} 118w,{% endif %} {% if media.preview_image.width >= 84 %}{{ media.preview_image | image_url: width: 84 }} 84w,{% endif %} {% if media.preview_image.width >= 168 %}{{ media.preview_image | image_url: width: 168 }} 168w,{% endif %} {% if media.preview_image.width >= 130 %}{{ media.preview_image | image_url: width: 130 }} 130w,{% endif %} {% if media.preview_image.width >= 260 %}{{ media.preview_image | image_url: width: 260 }} 260w{% endif %}" src="{{ media | image_url: width: 84, height: 84 }}" sizes="(min-width: 1200px) calc((1200px - 19.5rem) / 12), (min-width: 750px) calc((100vw - 16.5rem) / 8), calc((100vw - 8rem) / 5)" alt="{{ media.alt | escape }}" height="200" width="200" loading="lazy" > </button> </li> {%- endunless -%} {%- endfor -%} </ul> <button type="button" class="mobile-button slider-button slider-button--next{% if media_count <= 3 %} small-hide{% endif %}{% if media_count <= 4 %} medium-hide large-up-hide{% endif %}" name="next" aria-label="{{ 'general.slider.next_slide' | t }}" aria-controls="GalleryThumbnails-{{ section.id }}" data-step="3"> <svg class="icon" viewBox="0 0 15 15"> <path class="st0" d="M1.17,0.78l12.97,6.05c0.14,0.07,0.15,0.26,0.01,0.33L1.08,14.21c-0.2,0.11-0.4-0.13-0.26-0.29l4.46-6.78 c0.07-0.08,0.06-0.19-0.01-0.26l-4.34-5.8C0.77,0.94,0.96,0.69,1.17,0.78z"/> </svg> </button> </slider-component> {%- endif -%} {%- endif -%} </media-gallery> </div> <div class="product__info-wrapper grid__item{% if settings.page_width > 1400 and section.settings.media_size == "small" %} product__info-wrapper--extra-padding{% endif %}"> <div id="ProductInfo-{{ section.id }}" class="product__info-container{% if section.settings.enable_sticky_info %} product__info-container--sticky{% endif %}"> {%- assign product_form_id = 'product-form-' | append: section.id -%} {%- for block in section.blocks -%} {%- case block.type -%} {%- when '@app' -%} {% render block %} {%- when 'text' -%} <p class="product__text{% if block.settings.text_style == 'uppercase' %} caption-with-letter-spacing{% elsif block.settings.text_style == 'subtitle' %} subtitle{% endif %}" {{ block.shopify_attributes }}> {{- block.settings.text -}} </p> {%- when 'title' -%} <h1 class="product__title" {{ block.shopify_attributes }}> {{ product.title | escape }} </h1> {%- when 'price' -%} <div class="no-js-hidden" id="price-{{ section.id }}" role="status" {{ block.shopify_attributes }}> {%- render 'price', product: product, use_variant: true, show_badges: true, price_class: 'price--large' -%} </div> {%- if shop.taxes_included or shop.shipping_policy.body != blank -%} <div class="product__tax caption rte"> {%- if shop.taxes_included -%} {{ 'products.product.include_taxes' | t }} {%- endif -%} {%- if shop.shipping_policy.body != blank -%} {{ 'products.product.shipping_policy_html' | t: link: shop.shipping_policy.url }} {%- endif -%} </div> {%- endif -%} <div {{ block.shopify_attributes }}> {%- form 'product', product, id: 'product-form-installment', class: 'installment caption-large' -%} <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}"> {{ form | payment_terms }} {%- endform -%} </div> {%- when 'description' -%} {%- if product.description != blank -%} <div class="product__description rte"> {{ product.description }} </div> {%- endif -%} {%- when 'custom_liquid' -%} {{ block.settings.custom_liquid }} {%- when 'collapsible_tab' -%} <div class="product__accordion accordion" {{ block.shopify_attributes }}> <details id="Details-{{ block.id }}-{{ section.id }}"> <summary> <div class="summary__title"> {% render 'icon-accordion', icon: block.settings.icon %} <h2 class="h4 accordion__title"> {{ block.settings.heading | default: block.settings.page.title }} </h2> </div> {% render 'icon-caret' %} </summary> <div class="accordion__content rte" id="ProductAccordion-{{ block.id }}-{{ section.id }}"> {{ block.settings.content }} {{ block.settings.page.content }} </div> </details> </div> {%- when 'quantity_selector' -%} <div class="product-form__input product-form__quantity{% if settings.inputs_shadow_vertical_offset != 0 and settings.inputs_shadow_vertical_offset < 0 %} product-form__quantity-top{% endif %}" {{ block.shopify_attributes }}> <label class="form__label" for="Quantity-{{ section.id }}"> {{ 'products.product.quantity.label' | t }} </label> <quantity-input class="quantity"> <button class="quantity__button no-js-hidden" name="minus" type="button"> <span class="visually-hidden">{{ 'products.product.quantity.decrease' | t: product: product.title | escape }}</span> {% render 'icon-minus' %} </button> <input class="quantity__input" type="number" name="quantity" id="Quantity-{{ section.id }}" min="1" value="1" form="{{ product_form_id }}" > <button class="quantity__button no-js-hidden" name="plus" type="button"> <span class="visually-hidden">{{ 'products.product.quantity.increase' | t: product: product.title | escape }}</span> {% render 'icon-plus' %} </button> </quantity-input> </div> {%- when 'popup' -%} <modal-opener class="product-popup-modal__opener no-js-hidden" data-modal="#PopupModal-{{ block.id }}" {{ block.shopify_attributes }}> <button id="ProductPopup-{{ block.id }}" class="product-popup-modal__button link" type="button" aria-haspopup="dialog">{{ block.settings.text | default: block.settings.page.title }}</button> </modal-opener> <a href="{{ block.settings.page.url }}" class="product-popup-modal__button link no-js">{{ block.settings.text }}</a> {%- when 'share' -%} <share-button id="Share-{{ section.id }}" class="share-button" {{ block.shopify_attributes }}> <button class="share-button__button hidden"> {% render 'icon-share' %} {{ block.settings.share_label | escape }} </button> <details id="Details-{{ block.id }}-{{ section.id }}"> <summary class="share-button__button"> {% render 'icon-share' %} {{ block.settings.share_label | escape }} </summary> <div id="Product-share-{{ section.id }}" class="share-button__fallback motion-reduce"> <div class="field"> <span id="ShareMessage-{{ section.id }}" class="share-button__message hidden" role="status"> </span> <input type="text" class="field__input" id="url" value="{{ product.selected_variant.url | default: product.url | prepend: request.origin }}" placeholder="{{ 'general.share.share_url' | t }}" onclick="this.select();" readonly> <label class="field__label" for="url">{{ 'general.share.share_url' | t }}</label> </div> <button class="share-button__close hidden no-js-hidden"> {% render 'icon-close' %} <span class="visually-hidden">{{ 'general.share.close' | t }}</span> </button> <button class="share-button__copy no-js-hidden"> {% render 'icon-clipboard' %} <span class="visually-hidden">{{ 'general.share.copy_to_clipboard' | t }}</span> </button> </div> </details> </share-button> <script src="{{ 'share.js' | asset_url }}" defer="defer"></script> {%- when 'variant_picker' -%} {%- unless product.has_only_default_variant -%} {%- if block.settings.picker_type == 'button' -%} <variant-radios class="no-js-hidden" data-section="{{ section.id }}" data-url="{{ product.url }}" {{ block.shopify_attributes }}> {%- for option in product.options_with_values -%} <fieldset class="js product-form__input"> <legend class="form__label">{{ option.name }}</legend> {%- for value in option.values -%} <input type="radio" id="{{ section.id }}-{{ option.position }}-{{ forloop.index0 }}" name="{{ option.name }}" value="{{ value | escape }}" form="{{ product_form_id }}" {% if option.selected_value == value %}checked{% endif %} > <label for="{{ section.id }}-{{ option.position }}-{{ forloop.index0 }}"> {{ value }} </label> {%- endfor -%} </fieldset> {%- endfor -%} <script type="application/json"> {{ product.variants | json }} </script> </variant-radios> {%- else -%} <variant-selects class="no-js-hidden" data-section="{{ section.id }}" data-url="{{ product.url }}" {{ block.shopify_attributes }}> {%- for option in product.options_with_values -%} <div class="product-form__input product-form__input--dropdown"> <label class="form__label" for="Option-{{ section.id }}-{{ forloop.index0 }}"> {{ option.name }} </label> <div class="select"> <select id="Option-{{ section.id }}-{{ forloop.index0 }}" class="select__select" name="options[{{ option.name | escape }}]" form="{{ product_form_id }}" > {%- for value in option.values -%} <option value="{{ value | escape }}" {% if option.selected_value == value %}selected="selected"{% endif %}> {{ value }} </option> {%- endfor -%} </select> {% render 'icon-caret' %} </div> </div> {%- endfor -%} <script type="application/json"> {{ product.variants | json }} </script> </variant-selects> {%- endif -%} {%- endunless -%} <noscript class="product-form__noscript-wrapper-{{ section.id }}"> <div class="product-form__input{% if product.has_only_default_variant %} hidden{% endif %}"> <label class="form__label" for="Variants-{{ section.id }}">{{ 'products.product.product_variants' | t }}</label> <div class="select"> <select name="id" id="Variants-{{ section.id }}" class="select__select" form="{{ product_form_id }}"> {%- for variant in product.variants -%} <option {% if variant == product.selected_or_first_available_variant %}selected="selected"{% endif %} {% if variant.available == false %}disabled{% endif %} value="{{ variant.id }}" > {{ variant.title }} {%- if variant.available == false %} - {{ 'products.product.sold_out' | t }}{% endif %} - {{ variant.price | money | strip_html }} </option> {%- endfor -%} </select> {% render 'icon-caret' %} </div> </div> </noscript> {%- when 'buy_buttons' -%} <div {{ block.shopify_attributes }}> <product-form class="product-form"> <div class="product-form__error-message-wrapper" role="alert" hidden> <svg aria-hidden="true" focusable="false" role="presentation" class="icon icon-error" viewBox="0 0 13 13"> <circle cx="6.5" cy="6.50049" r="5.5" stroke="white" stroke-width="2"/> <circle cx="6.5" cy="6.5" r="5.5" fill="#EB001B" stroke="#EB001B" stroke-width="0.7"/> <path d="M5.87413 3.52832L5.97439 7.57216H7.02713L7.12739 3.52832H5.87413ZM6.50076 9.66091C6.88091 9.66091 7.18169 9.37267 7.18169 9.00504C7.18169 8.63742 6.88091 8.34917 6.50076 8.34917C6.12061 8.34917 5.81982 8.63742 5.81982 9.00504C5.81982 9.37267 6.12061 9.66091 6.50076 9.66091Z" fill="white"/> <path d="M5.87413 3.17832H5.51535L5.52424 3.537L5.6245 7.58083L5.63296 7.92216H5.97439H7.02713H7.36856L7.37702 7.58083L7.47728 3.537L7.48617 3.17832H7.12739H5.87413ZM6.50076 10.0109C7.06121 10.0109 7.5317 9.57872 7.5317 9.00504C7.5317 8.43137 7.06121 7.99918 6.50076 7.99918C5.94031 7.99918 5.46982 8.43137 5.46982 9.00504C5.46982 9.57872 5.94031 10.0109 6.50076 10.0109Z" fill="white" stroke="#EB001B" stroke-width="0.7"/> </svg> <span class="product-form__error-message"></span> </div> {%- form 'product', product, id: product_form_id, class: 'form', novalidate: 'novalidate', data-type: 'add-to-cart-form' -%} <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}" disabled> <div class="product-form__buttons"> <button type="submit" name="add" class="product-form__submit button button--full-width {% if block.settings.show_dynamic_checkout and product.selling_plan_groups == empty %}button--secondary{% else %}button--primary{% endif %}" {% if product.selected_or_first_available_variant.available == false %}disabled{% endif %} > <span> {%- if product.selected_or_first_available_variant.available -%} {{ 'products.product.add_to_cart' | t }} {%- else -%} {{ 'products.product.sold_out' | t }} {%- endif -%} </span> <div class="loading-overlay__spinner hidden"> <svg aria-hidden="true" focusable="false" role="presentation" class="spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"> <circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle> </svg> </div> </button> {%- if block.settings.show_dynamic_checkout -%} {{ form | payment_button }} {%- endif -%} </div> {%- endform -%} </product-form> {{ 'component-pickup-availability.css' | asset_url | stylesheet_tag }} {%- assign pick_up_availabilities = product.selected_or_first_available_variant.store_availabilities | where: 'pick_up_enabled', true -%} <pickup-availability class="product__pickup-availabilities no-js-hidden" {% if product.selected_or_first_available_variant.available and pick_up_availabilities.size > 0 %} available{% endif %} data-root-url="{{ routes.root_url }}" data-variant-id="{{ product.selected_or_first_available_variant.id }}" data-has-only-default-variant="{{ product.has_only_default_variant }}"> <template> <pickup-availability-preview class="pickup-availability-preview"> {% render 'icon-unavailable' %} <div class="pickup-availability-info"> <p class="caption-large">{{ 'products.product.pickup_availability.unavailable' | t }}</p> <button class="pickup-availability-button link link--text underlined-link">{{ 'products.product.pickup_availability.refresh' | t }}</button> </div> </pickup-availability-preview> </template> </pickup-availability> </div> <script src="{{ 'pickup-availability.js' | asset_url }}" defer="defer"></script> {%- when 'rating' -%} {%- if product.metafields.reviews.rating.value != blank -%} {% liquid assign rating_decimal = 0 assign decimal = product.metafields.reviews.rating.value.rating | modulo: 1 if decimal >= 0.3 and decimal <= 0.7 assign rating_decimal = 0.5 elsif decimal > 0.7 assign rating_decimal = 1 endif %} <div class="rating" role="img" aria-label="{{ 'accessibility.star_reviews_info' | t: rating_value: product.metafields.reviews.rating.value, rating_max: product.metafields.reviews.rating.value.scale_max }}"> <span aria-hidden="true" class="rating-star color-icon-{{ settings.accent_icons }}" style="--rating: {{ product.metafields.reviews.rating.value.rating | floor }}; --rating-max: {{ product.metafields.reviews.rating.value.scale_max }}; --rating-decimal: {{ rating_decimal }};"></span> </div> <p class="rating-text caption"> <span aria-hidden="true">{{ product.metafields.reviews.rating.value }} / {{ product.metafields.reviews.rating.value.scale_max }}</span> </p> <p class="rating-count caption"> <span aria-hidden="true">({{ product.metafields.reviews.rating_count }})</span> <span class="visually-hidden">{{ product.metafields.reviews.rating_count }} {{ "accessibility.total_reviews" | t }}</span> </p> {%- endif -%} {%- endcase -%} {%- endfor -%} </div> </div> </div> <product-modal id="ProductModal-{{ section.id }}" class="product-media-modal media-modal"> <div class="product-media-modal__dialog" role="dialog" aria-label="{{ 'products.modal.label' | t }}" aria-modal="true" tabindex="-1"> <button id="ModalClose-{{ section.id }}" type="button" class="product-media-modal__toggle" aria-label="{{ 'accessibility.close' | t }}">{% render 'icon-close' %}</button> <div class="product-media-modal__content" role="document" aria-label="{{ 'products.modal.label' | t }}" tabindex="0"> {%- liquid if product.selected_or_first_available_variant.featured_media != null assign media = product.selected_or_first_available_variant.featured_media render 'product-media', media: media, loop: section.settings.enable_video_looping, variant_image: section.settings.hide_variants endif -%} {%- for media in product.media -%} {%- liquid if section.settings.hide_variants and variant_images contains media.src assign variant_image = true else assign variant_image = false endif unless media.id == product.selected_or_first_available_variant.featured_media.id render 'product-media', media: media, loop: section.settings.enable_video_looping, variant_image: variant_image endunless -%} {%- endfor -%} </div> </div> </product-modal> {% assign popups = section.blocks | where: "type", "popup" %} {%- for block in popups -%} <modal-dialog id="PopupModal-{{ block.id }}" class="product-popup-modal" {{ block.shopify_attributes }}> <div role="dialog" aria-label="{{ block.settings.text }}" aria-modal="true" class="product-popup-modal__content" tabindex="-1"> <button id="ModalClose-{{ block.id }}" type="button" class="product-popup-modal__toggle" aria-label="{{ 'accessibility.close' | t }}">{% render 'icon-close' %}</button> <div class="product-popup-modal__content-info"> <h1 class="h2">{{ block.settings.page.title }}</h1> {{ block.settings.page.content }} </div> </div> </modal-dialog> {%- endfor -%} </section> {% javascript %} class ProductModal extends ModalDialog { constructor() { super(); } hide() { super.hide(); } show(opener) { super.show(opener); this.showActiveMedia(); } showActiveMedia() { this.querySelectorAll(`[data-media-id]:not([data-media-id="${this.openedBy.getAttribute("data-media-id")}"])`).forEach((element) => { element.classList.remove('active'); } ) const activeMedia = this.querySelector(`[data-media-id="${this.openedBy.getAttribute("data-media-id")}"]`); const activeMediaTemplate = activeMedia.querySelector('template'); const activeMediaContent = activeMediaTemplate ? activeMediaTemplate.content : null; activeMedia.classList.add('active'); activeMedia.scrollIntoView(); const container = this.querySelector('[role="document"]'); container.scrollLeft = (activeMedia.width - container.clientWidth) / 2; if (activeMedia.nodeName == 'DEFERRED-MEDIA' && activeMediaContent && activeMediaContent.querySelector('.js-youtube')) activeMedia.loadContent(); } } if (!customElements.get('product-modal')) customElements.define('product-modal', ProductModal); {% endjavascript %} {% if product.media.size > 0 %} <script src="{{ 'media-gallery.js' | asset_url }}" defer="defer"></script> {% endif %} <script> document.addEventListener('DOMContentLoaded', function() { function isIE() { const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); const trident = ua.indexOf('Trident/'); return (msie > 0 || trident > 0); } if (!isIE()) return; const hiddenInput = document.querySelector('#{{ product_form_id }} input[name="id"]'); const noScriptInputWrapper = document.createElement('div'); const variantSwitcher = document.querySelector('variant-radios[data-section="{{ section.id }}"]') || document.querySelector('variant-selects[data-section="{{ section.id }}"]'); noScriptInputWrapper.innerHTML = document.querySelector('.product-form__noscript-wrapper-{{ section.id }}').textContent; variantSwitcher.outerHTML = noScriptInputWrapper.outerHTML; document.querySelector('#Variants-{{ section.id }}').addEventListener('change', function(event) { hiddenInput.value = event.currentTarget.value; }); }); </script> {%- if first_3d_model -%} <script type="application/json" id="ProductJSON-{{ product.id }}"> {{ product.media | where: 'media_type', 'model' | json }} </script> <script src="{{ 'product-model.js' | asset_url }}" defer></script> {%- endif -%} {%- liquid if product.selected_or_first_available_variant.featured_media assign seo_media = product.selected_or_first_available_variant.featured_media else assign seo_media = product.featured_media endif -%} <script type="application/ld+json"> { "@context": "http://schema.org/", "@type": "Product", "name": {{ product.title | json }}, "url": {{ request.origin | append: product.url | json }}, {% if seo_media -%} {%- assign media_size = seo_media.preview_image.width | append: 'x' -%} "image": [ {{ seo_media | img_url: media_size | prepend: "https:" | json }} ], {%- endif %} "description": {{ product.description | strip_html | json }}, {% if product.selected_or_first_available_variant.sku != blank -%} "sku": {{ product.selected_or_first_available_variant.sku | json }}, {%- endif %} "brand": { "@type": "Thing", "name": {{ product.vendor | json }} }, "offers": [ {%- for variant in product.variants -%} { "@type" : "Offer", {%- if variant.sku != blank -%} "sku": {{ variant.sku | json }}, {%- endif -%} "availability" : "http://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}", "price" : {{ variant.price | divided_by: 100.00 | json }}, "priceCurrency" : {{ cart.currency.iso_code | json }}, "url" : {{ request.origin | append: variant.url | json }} }{% unless forloop.last %},{% endunless %} {%- endfor -%} ] } </script> {% schema %} { "name": "t:sections.main-product.name", "tag": "section", "class": "section", "blocks": [ { "type": "@app" }, { "type": "text", "name": "t:sections.main-product.blocks.text.name", "settings": [ { "type": "text", "id": "text", "default": "Text block", "label": "t:sections.main-product.blocks.text.settings.text.label" }, { "type": "select", "id": "text_style", "options": [ { "value": "body", "label": "t:sections.main-product.blocks.text.settings.text_style.options__1.label" }, { "value": "subtitle", "label": "t:sections.main-product.blocks.text.settings.text_style.options__2.label" }, { "value": "uppercase", "label": "t:sections.main-product.blocks.text.settings.text_style.options__3.label" } ], "default": "body", "label": "t:sections.main-product.blocks.text.settings.text_style.label" } ] }, { "type": "title", "name": "t:sections.main-product.blocks.title.name", "limit": 1 }, { "type": "price", "name": "t:sections.main-product.blocks.price.name", "limit": 1 }, { "type": "quantity_selector", "name": "t:sections.main-product.blocks.quantity_selector.name", "limit": 1 }, { "type": "variant_picker", "name": "t:sections.main-product.blocks.variant_picker.name", "limit": 1, "settings": [ { "type": "select", "id": "picker_type", "options": [ { "value": "dropdown", "label": "t:sections.main-product.blocks.variant_picker.settings.picker_type.options__1.label" }, { "value": "button", "label": "t:sections.main-product.blocks.variant_picker.settings.picker_type.options__2.label" } ], "default": "button", "label": "t:sections.main-product.blocks.variant_picker.settings.picker_type.label" } ] }, { "type": "buy_buttons", "name": "t:sections.main-product.blocks.buy_buttons.name", "limit": 1, "settings": [ { "type": "checkbox", "id": "show_dynamic_checkout", "default": true, "label": "t:sections.main-product.blocks.buy_buttons.settings.show_dynamic_checkout.label", "info": "t:sections.main-product.blocks.buy_buttons.settings.show_dynamic_checkout.info" } ] }, { "type": "description", "name": "t:sections.main-product.blocks.description.name", "limit": 1 }, { "type": "share", "name": "t:sections.main-product.blocks.share.name", "limit": 1, "settings": [ { "type": "text", "id": "share_label", "label": "t:sections.main-product.blocks.share.settings.text.label", "default": "Share" }, { "type": "paragraph", "content": "t:sections.main-product.blocks.share.settings.featured_image_info.content" }, { "type": "paragraph", "content": "t:sections.main-product.blocks.share.settings.title_info.content" } ] }, { "type": "custom_liquid", "name": "t:sections.main-product.blocks.custom_liquid.name", "settings": [ { "type": "liquid", "id": "custom_liquid", "label": "t:sections.main-product.blocks.custom_liquid.settings.custom_liquid.label", "info": "t:sections.main-product.blocks.custom_liquid.settings.custom_liquid.info" } ] }, { "type": "collapsible_tab", "name": "t:sections.main-product.blocks.collapsible_tab.name", "settings": [ { "type": "text", "id": "heading", "default": "Collapsible row", "info": "t:sections.main-product.blocks.collapsible_tab.settings.heading.info", "label": "t:sections.main-product.blocks.collapsible_tab.settings.heading.label" }, { "type": "select", "id": "icon", "options": [ { "value": "none", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__1.label" }, { "value": "apple", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__2.label" }, { "value": "banana", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__3.label" }, { "value": "bottle", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__4.label" }, { "value": "box", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__5.label" }, { "value": "carrot", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__6.label" }, { "value": "chat_bubble", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__7.label" }, { "value": "check_mark", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__8.label" }, { "value": "clipboard", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__9.label" }, { "value": "dairy", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__10.label" }, { "value": "dairy_free", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__11.label" }, { "value": "dryer", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__12.label" }, { "value": "eye", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__13.label" }, { "value": "fire", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__14.label" }, { "value": "gluten_free", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__15.label" }, { "value": "heart", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__16.label" }, { "value": "iron", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__17.label" }, { "value": "leaf", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__18.label" }, { "value": "leather", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__19.label" }, { "value": "lightning_bolt", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__20.label" }, { "value": "lipstick", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__21.label" }, { "value": "lock", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__22.label" }, { "value": "map_pin", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__23.label" }, { "value": "nut_free", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__24.label" }, { "value": "pants", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__25.label" }, { "value": "paw_print", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__26.label" }, { "value": "pepper", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__27.label" }, { "value": "perfume", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__28.label" }, { "value": "plane", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__29.label" }, { "value": "plant", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__30.label" }, { "value": "price_tag", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__31.label" }, { "value": "question_mark", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__32.label" }, { "value": "recycle", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__33.label" }, { "value": "return", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__34.label" }, { "value": "ruler", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__35.label" }, { "value": "serving_dish", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__36.label" }, { "value": "shirt", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__37.label" }, { "value": "shoe", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__38.label" }, { "value": "silhouette", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__39.label" }, { "value": "snowflake", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__40.label" }, { "value": "star", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__41.label" }, { "value": "stopwatch", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__42.label" }, { "value": "truck", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__43.label" }, { "value": "washing", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.options__44.label" } ], "default": "check_mark", "label": "t:sections.main-product.blocks.collapsible_tab.settings.icon.label" }, { "type": "richtext", "id": "content", "label": "t:sections.main-product.blocks.collapsible_tab.settings.content.label" }, { "type": "page", "id": "page", "label": "t:sections.main-product.blocks.collapsible_tab.settings.page.label" } ] }, { "type": "popup", "name": "t:sections.main-product.blocks.popup.name", "settings": [ { "type": "text", "id": "text", "default": "Pop-up link text", "label": "t:sections.main-product.blocks.popup.settings.link_label.label" }, { "id": "page", "type": "page", "label": "t:sections.main-product.blocks.popup.settings.page.label" } ] }, { "type": "rating", "name": "t:sections.main-product.blocks.rating.name", "limit": 1, "settings": [ { "type": "paragraph", "content": "t:sections.main-product.blocks.rating.settings.paragraph.content" } ] } ], "settings": [ { "type": "checkbox", "id": "enable_sticky_info", "default": true, "label": "t:sections.main-product.settings.enable_sticky_info.label" }, { "type": "header", "content": "t:sections.main-product.settings.header.content", "info": "t:sections.main-product.settings.header.info" }, { "type": "select", "id": "media_size", "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": "large", "label": "t:sections.main-product.settings.media_size.options__3.label" } ], "default": "large", "label": "t:sections.main-product.settings.media_size.label", "info": "t:sections.main-product.settings.media_size.info" }, { "type": "select", "id": "mobile_thumbnails", "options": [ { "value": "show", "label": "t:sections.main-product.settings.mobile_thumbnails.options__1.label" }, { "value": "hide", "label": "t:sections.main-product.settings.mobile_thumbnails.options__2.label" } ], "default": "hide", "label": "t:sections.main-product.settings.mobile_thumbnails.label" }, { "type": "checkbox", "id": "hide_variants", "default": false, "label": "t:sections.main-product.settings.hide_variants.label" }, { "type": "checkbox", "id": "enable_video_looping", "default": false, "label": "t:sections.main-product.settings.enable_video_looping.label" }, { "type": "checkbox", "id": "hide_buttons", "default": false, "label": "Hide main image buttons in mobile" }, { "type": "color", "id": "button_color", "default": "#fff", "label": "Button color" }, { "type": "header", "content": "t:sections.all.padding.section_padding_heading" }, { "type": "range", "id": "padding_top", "min": 0, "max": 100, "step": 4, "unit": "px", "label": "t:sections.all.padding.padding_top", "default": 36 }, { "type": "range", "id": "padding_bottom", "min": 0, "max": 100, "step": 4, "unit": "px", "label": "t:sections.all.padding.padding_bottom", "default": 36 } ] } {% endschema %} |
3. We have to add a css file just for our slider. Open the "section-main-product.css" under the Assets folder and replace the code with the code below.
.product { @supports (-webkit-touch-callout: none) { |
4. Next, we have to add our own Javascript code. Open the global.js under the Assets folder and paste the code at the very bottom of the file.
class VerticalSliderComponent extends SliderComponent { constructor() { super(); this.mediaCount = this.dataset.mediaCount; } initPages() { this.sliderItemsToShow = Array.from(this.sliderItems).filter(element => element.clientWidth > 0); if (this.sliderItemsToShow.length < 2) return; this.sliderItemOffset = this.sliderItemsToShow[1].offsetTop - this.sliderItemsToShow[0].offsetTop; this.slidesPerPage = Math.floor(this.slider.clientWidth / this.sliderItemOffset); this.totalPages = this.sliderItemsToShow.length - this.slidesPerPage + 1; this.update(); } currentSlide(current) { const thumb = this.querySelector(`[data-media-position='${current}']`); if(current == -1) return; if(this.mediaCount > 5) { const offHeight = Math.floor(this.sliderItemOffset * current); this.slider.scrollTo( { top: offHeight }); } } isSlideVisible(element, offset = 0) { const lastVisibleSlide = this.slider.clientWidth + this.slider.scrollTop - offset; return (element.offsetTop + element.clientWidth) <= lastVisibleSlide && element.offsetTop >= this.slider.scrollTop; } onButtonClick(event) { event.preventDefault(); const step = event.currentTarget.dataset.step || 1; this.slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollTop + (step * this.sliderItemOffset) : this.slider.scrollTop - (step * this.sliderItemOffset); this.slider.scrollTo({ top: this.slideScrollPosition }); } } customElements.define('vertical-slider-component', VerticalSliderComponent); class MainSliderComponent extends SliderComponent { constructor() { super(); this.thumbSlider = document.querySelector('vertical-slider-component'); this.horizontalSlider = document.querySelector('[mobile-slider]'); this.sliderButtons = this.horizontalSlider.querySelectorAll('li'); this.sliderButtons.forEach(btn => btn.addEventListener('click', this.thumbClicked.bind(this))) this.zoom(); } onButtonClick(event) { event.preventDefault(); const step = event.currentTarget.dataset.step || 1; this.slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollLeft + (step * this.sliderItemOffset) : this.slider.scrollLeft - (step * this.sliderItemOffset); this.slider.scrollTo({ left: this.slideScrollPosition }); const current = Math.floor(this.slideScrollPosition / this.sliderItemsToShow[1].clientWidth) console.log(current); this.thumbSlider.currentSlide(current); this.currentSlide(current); } zoom() { this.zoomItems = this.querySelectorAll('modal-opener'); this.zoomItems.forEach(item => { const mediaSmall = window.matchMedia( '( min-width: 481px )' ); item.style.overflow = 'hidden'; const zoomImg = item.querySelector('.product__media');; if (mediaSmall.matches) { item.addEventListener("mouseout", function() { zoomImg.style.transform = 'scale(1)'; }); item.addEventListener("mouseover", function() { zoomImg.style.transform = 'scale(1.8)'; }) item.addEventListener("mousemove", function(e) { const itemWidth =zoomImg.getBoundingClientRect().width; const itemLeft = zoomImg.getBoundingClientRect().left; const itemHt = zoomImg.getBoundingClientRect().height; const itemTop = zoomImg.getBoundingClientRect().top + window.scrollY; const translateX = Math.round((e.pageX - itemLeft ) / itemWidth * 100); const translateY = Math.round((e.pageY - itemTop) / itemHt * 100); zoomImg.style.transformOrigin = `${translateX}% ${translateY}%`; }); } }); } thumbClicked(e) { const currentTarget = e.currentTarget.dataset.target; this.horizontalSlider.querySelectorAll('button').forEach((btn) => btn.removeAttribute('aria-current')); e.currentTarget.querySelector('button').setAttribute('aria-current', true); const mainImage = document.querySelector(`[data-media-id='${currentTarget}']`); const parent = mainImage.parentNode; const offsetLeft = mainImage.offsetLeft; parent.scrollTo( { left: offsetLeft }); } currentSlide(current) { if(current == -1){ current = 0}; const offLeft = Math.floor(this.horizontalSlider.sliderItemOffset * current); this.horizontalSlider.slider.scrollTo( { left: offLeft }); } } customElements.define('main-slider-component', MainSliderComponent); |
Please skip step #5 in the video regarding vertical-media-gallery.js.
That's it. I hope it didn't make you sweat. (",)
Update 2/2/22: When store owners added another section that contains a component slider, the code that we have here is being replace. So I just fix the naming convention to fix it.
Update 3/23/22: I added the icons in the collapsible component that was added in Dawn 4.0 version. I made major change actually. If you notice, the original Dawn code does not really look good with IOS devices. I was able to make this slider somewhat decent in the IOS users.
10 comments
@Max Be aware that with the new version of Dawn, a thumbnail slider is already available, that is the reason I only created a vertical slider
Hi there!
Is it possible to also have a horizontal slider unter the picture?
Because vertical doesn’t look well for my page.
Kind regards
Max
Hi, why vertical-media-gallery.js is not included/referenced in main-product.liquid? also on iOS devices, after implementing the code, I was not able to swipe up and down on product pages. Dawn version 4.0.0
Can you tell me how can we make them Fade effect on slides?
Please make sure you are copying the code. The javascript is responsible for buttons to work. If this doesn’t work then you are missing that code or might have an error.