How to Create a Product Image Slider on Dawn Theme

Follow Below steps to create product image slider in shopify dawn theme:

  1. Go to theme -> Action -> Edit Code -> Layout -> theme.liquid
  2. Find </head> and paste below code just above this close head tag
<!-- CSS -->
<link rel="stylesheet" href="https://unpkg.com/flickity@2/dist/flickity.min.css">
<!-- JavaScript -->
<script src="https://unpkg.com/flickity@2/dist/flickity.pkgd.min.js"></script> 

3. Open Sections -> main-product.liquid and find below code.

<slider-component class="slider-mobile-gutter">
    <a class="skip-to-content-link button visually-hidden" href="#ProductInfo-{{ section.id }}">
      {{ "accessibility.skip_to_product_info" | t }}
    </a>
    <ul class="product__media-list grid grid--peek list-unstyled slider slider--mobile" role="list">
      {%- assign variant_images = product.images | where: 'attached_to_variant?', true | map: 'src' -%}
      {%- if product.selected_or_first_available_variant.featured_media != null -%}
        {%- assign media = product.selected_or_first_available_variant.featured_media -%}
        <li class="product__media-item grid__item slider__slide{% 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 }}">
          {% render 'product-thumbnail', media: media, position: 'featured', loop: section.settings.enable_video_looping, modal_id: section.id, xr_button: true %}
        </li>
      {%- endif -%}
      {%- for media in product.media -%}
        {%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
          <li class="product__media-item grid__item slider__slide{% 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 }}">
            {% render 'product-thumbnail', media: media, position: forloop.index, loop: section.settings.enable_video_looping, modal_id: section.id, xr_button: true %}
          </li>
        {%- endunless -%}
      {%- endfor -%}
    </ul>
    <div class="slider-buttons no-js-hidden{% if product.media.size < 2 %} small-hide{% endif %}">
      <button type="button" class="slider-button slider-button--prev" name="previous" aria-label="{{ 'accessibility.previous_slide' | t }}">{% render 'icon-caret' %}</button>
      <div class="slider-counter caption">
        <span class="slider-counter--current">1</span>
        <span aria-hidden="true"> / </span>
        <span class="visually-hidden">{{ 'accessibility.of' | t }}</span>
        <span class="slider-counter--total">{% if section.settings.hide_variants %}{{ product.media.size | minus: variant_images.size | plus: 1 }}{% else %}{{ product.media.size }}{% endif %}</span>
      </div>
      <button type="button" class="slider-button slider-button--next" name="next" aria-label="{{ 'accessibility.next_slide' | t }}">{% render 'icon-caret' %}</button>
    </div>
</slider-component> 

4. Replace above code with below code.

{% if product.images != null %}
    <div class="carousel product__image-slider" role="document" aria-label="{{ 'products.modal.label' | t }}" tabindex="0">
      {%- for media in product.media -%}
      <div class="carousel-cell">
        {% if media.media_type == "video" %}
        {{ media | media_tag: image_size: "720x", autoplay: true, loop: loop, controls: true, preload: "none", autoplay: true, height: "100%", width: "100%" }}
        {% else %}
        <img data-flickity-lazyload-srcset="
                                            {{ media | img_url: '1440x' }} 1440w,
                                            {{ media | img_url: '1080x' }} 1080w,
                                            {{ media | img_url: '720x' }} 767w,
                                            {{ media | img_url: '480x' }} 480w"
             sizes="(min-width: 480px) 1440px, 1080px, 767px, 480px"
             data-flickity-lazyload-src="{{ media | img_url: 'master' }}"
             alt="{{ media.alt | escape }}"
             border="0"
             data-media-id="{{ media.id }}" />
        {% endif %}
      </div>
      {%- endfor -%}
      <!--- Coded by bluish.io --->
    </div>
    {% if product.images.size > 1 %}
    <div class="carousel product__image-thumbnail">
      {%- for media in product.media -%}
      <div class="carousel-cell">
        <img data-flickity-lazyload-srcset="
                                            {{ media | img_url: '1440x' }} 1440w,
                                            {{ media | img_url: '1080x' }} 1080w,
                                            {{ media | img_url: '720x' }} 767w,
                                            {{ media | img_url: '480x' }} 480w"
             sizes="(min-width: 480px) 1440px, 1080px, 767px, 480px"
             data-flickity-lazyload-src="{{ media | img_url: 'master' }}"
             alt="{{ media.alt | escape }}"
             border="0"
             data-media-id="{{ media_id }}" />
      </div>
      {%- endfor -%}
      <!--- Coded by ramzanmalik.in --->
    </div>
    {% endif %}
    {% else %}
    {{ 'product-1' | placeholder_svg_tag }}
  {% endif %} 

5. Find {% schema %} in the same main-product.liquid file and paste below code just above {% schema %}

<script>
  var elem = document.querySelector('.carousel.product__image-slider');
  var flkty = new Flickity( elem, {
    contain: true,
    imagesLoaded: true,
    lazyLoad: 1,
    wrapAround: true,
    pageDots: false,
    {% if product.images.size < 2 %}
    prevNextButtons: false,
    {% endif %}
    adaptiveHeight: true
  });
  var elemThumbnail = document.querySelector('.carousel.product__image-thumbnail');
  var flktyB = new Flickity( elemThumbnail, {
    asNavFor: '.carousel.product__image-slider',
    contain: true,
    imagesLoaded: true,
    lazyLoad: 4,
    pageDots: false,
    prevNextButtons: false
  });
</script> 

6. Go to Assets -> base.css and paste below code at last.

.product__image-slider {
  width: 100%;
}
.product__image-slider .carousel-cell {
  width: 100%;
  height: auto;
  margin: 0 5px
}
.product__image-slider .carousel-cell img {
  width: 100%;
  height: 100%;
}
.grid__item .product__image-slider {
  margin-bottom: 2rem;
}
.product__image-slider .flickity-viewport {
  transition: height 0.2s;
}
.flickity-button:disabled {
  display: none;
}
.product__image-thumbnail .carousel-cell {
  width: 20%;
  margin-right: 10px;
}
.product__image-thumbnail .carousel-cell img {
  width: 100%;
  height: 100%;
}
@media screen and (min-width: 990px) {
  .product:not(.product--no-media):not(.featured-product) .product__media-wrapper {
    max-width: 50% !important;
    width: calc(50% - 1rem / 2) !important;
  }
  .product:not(.product--no-media):not(.featured-product) .product__info-wrapper {
    max-width: 50% !important;
    width: calc(50% - 1rem / 2) !important;
  }
} 

7. Go to Assets -> global.js and find below code

constructor() {
    super();
    this.addEventListener('change', this.onVariantChange);
} 

8. Replace above code with below code.

constructor() {
    super();
    this.initLoad();
    this.addEventListener('change', this.onVariantChange);
}
initLoad(){
    this.updateOptions();
    this.updateMasterId();
    this.updateMedia();
} 

9. Find below code in the same global.js file.

updateMedia() {
    if (!this.currentVariant) return;
    if (!this.currentVariant.featured_media) return;
    const newMedia = document.querySelector(
      `[data-media-id="${this.dataset.section}-${this.currentVariant.featured_media.id}"]`
    );
    if (!newMedia) return;
    const modalContent = document.querySelector(`#ProductModal-${this.dataset.section} .product-media-modal__content`);
    const newMediaModal = modalContent.querySelector( `[data-media-id="${this.currentVariant.featured_media.id}"]`);
    const parent = newMedia.parentElement;
    if (parent.firstChild == newMedia) return;
    modalContent.prepend(newMediaModal);
    parent.prepend(newMedia);
    this.stickyHeader = this.stickyHeader || document.querySelector('sticky-header');
    if(this.stickyHeader) {
      this.stickyHeader.dispatchEvent(new Event('preventHeaderReveal'));
    }
    window.setTimeout(() => { parent.querySelector('li.product__media-item').scrollIntoView({behavior: "smooth"}); });
} 

10. Replace above code with below code.

updateMedia() {
    if (!this.currentVariant) return;
    if (!this.currentVariant.featured_media) return;
    var current_media_id = this.currentVariant.featured_media.id;
    // For product page with flickity
    if (document.querySelector('.product__image-slider')) {
      var media_len = document.querySelector('.product__image-slider .flickity-slider').childElementCount;
      var media_id_array = [];
      for (let i = 0; i < media_len; i++) {
        media_id_array.push(parseInt(document.querySelector('.product__image-slider').querySelectorAll("img")[i].getAttribute('data-media-id')));
      }
      flkty.select(media_id_array.indexOf(current_media_id));
    }
    // For featured products on homepage without flickity
    else if (document.querySelector('.product__media-list')) {
      var featured_product_media_len = document.querySelector('.product__media-list').childElementCount;
      for (let i = 0; i < featured_product_media_len; i++) {
        var child = document.querySelector('.product__media-list').children[i];
        if (child.getAttribute('data-media-id').indexOf(current_media_id) > 0) {
          child.style.display = "block";
        } else {
          child.style.display = "none";
        }
      }
    }
}