0

i started my online shop a few months back and since im not a programmer i finally reached a point where i need help.

I was trying to improve Pagespeed according to Google and i managed to get the CLS of my Index page to almost 0. - Lazyload, System Fonts, Aspect Ratio of Pictures and so on... i managed to find it all in the Liquid Code of my Shopify Store and optimize it by myself. ... ok i totally crashed the page a few times but thats what backups are for ;-)

But i have no Idea about the Product Pages. Lighthouse can tell me the Objects causing a CLS of almost 1(!) - mostly its around 0,9 - but i looked at the code of these Objects for 5 days now and i am lost. its supposed to be caused by the following:

  • main#MainContent.main-content
  • div.col-12.col-md-6.order-1
  • div.page-container.page-element.is-moved-by-drawer

...and sometimes a few more causing small numbers ob CLS

i tried diffrent Templates of my theme but no change i googled a lot... and i mean A LOT in 5 days... i tried many suggestions that i found while googling about this issue i tried to find Code checkers or HTML code Analyzer but that didnt help i tried things mentioned in this thread: Cumulative Layout Shift with Bootstrap 4 grid

i dont see the Layout shift supposedly caused by these elements the only Layout shift i can see is somehow NOT mentioned by google - but results don`t change if i remove the app causing the layout shift that i can see. (app is called Legal Pro - needed for keeping the shop up to the requirements of German law. it injects Product data between the Price and the remaining Stock. I can not change the app, only deactivate it. But even when the app is removed, the Lighthouse data does not change. Not even a little!

i would be willing to totally replace the code of the product page with a code with less CLS but adapting complete new code to the existing pages is out of my league. I can change it but not write it. :-/

I`d be very thankful for any help. Just give me something i can google.

(and yes i know there are more issues on that page but CLS is giving me a bad Google ranking right now - one problem at the time)

an example Page can be found here: https://besonderes-mit-liebe-handgemacht.de/collections/mannergeschenke/products/edle-kugelschreiber-von-hand-gedrechselt-besondere-holzer

the Product page code for the Page is this:

<div class="row product-single">
    <div class="col-md-6">
      <div class="photos">
        <div class="photos__item photos__item--main">
          {%- assign featured_image = current_variant.featured_image | default: product.featured_image -%}
          {%- for image in product.images -%}
          <div class="product-single__photo product__photo-container product__photo-container-{{ section.id }} js{% unless image == featured_image %} hide{% endunless %}"
               id="ProductPhoto"
               style="max-width: {% include 'image-width' with image: image, width: 480 %}px;"
               data-image-id="{{ image.id }}">
              <a href="{{ image | img_url: '1200x1200' }}" data-lightbox="image-product"
               class="product__photo-wrapper product__photo-wrapper-{{ section.id }}"
               style="padding-top:{{ 1 | divided_by: image.aspect_ratio | times: 100}}%;">
              {% assign img_url = image | img_url: '1x1' | replace: '_1x1.', '_{width}x.' %}
              <img class="lazyload {% unless image == featured_image %} lazypreload{% endunless %}"
                   src="{{ image | img_url: '1024x' }}"
                   data-src="{{ img_url }}"
                   data-widths="[180, 240, 360, 480, 720, 960, 1080, 1296, 1512, 1728, 2048]"
                   data-aspectratio="{{ image.aspect_ratio }}"
                   data-sizes="auto"
                   alt="{{ image.alt | escape }}">
            </a>
          </div>
          {%- endfor -%}
          <noscript>
              <a href="{{ featured_image | img_url: '1200x1200' }}">
              <img src="{{ featured_image | img_url: product_image_size }}" alt="{{ featured_image.alt | escape }}" id="ProductPhotoImg-{{ section.id }}">
            </a>
          </noscript>
          {% include 'productVideo' %}
        </div>
        {%- if product.images.size > 1 -%}
        <div class="photos__item photos__item--thumbs">
          <div class="product-single__thumbnails product-single__thumbnails-{{ section.id }} product-single__thumbnails--static">
            {%- for image in product.images -%}
            <div class="product-single__thumbnail-item product-single__thumbnail-item-{{ section.id }}{% if image == featured_image %} is-active{% endif %}" data-image-id="{{ image.id }}">
              <a href="{{ image.src | img_url: product_image_size }}" data-zoom="{{ image.src | img_url: '1200x1200' }}" class="product-single__thumbnail product-single__thumbnail-{{ section.id }}">
                    {% assign img_url = image | img_url: '1x1' | replace: '_1x1.', '_{width}x.' %}
                    <img class="lazyload {% unless image == featured_image %} lazypreload{% endunless %}"
                         src="{{ image | img_url: '1024x' }}"
                         data-src="{{ img_url }}"
                         data-widths="[180, 240, 360, 480, 720, 960, 1080, 1296, 1512, 1728, 2048]"
                         data-aspectratio="{{ image.aspect_ratio }}"
                         data-sizes="auto"
                         alt="{{ image.alt | escape }}">
              </a>
            </div>
            {%- endfor -%}
          </div>
        </div>
        <script >
          {%- capture arrow_left -%}{%- include 'icon-arrow-left' -%}{%- endcapture -%}
          {%- capture arrow_right -%}{%- include 'icon-arrow-right' -%}{%- endcapture -%}
          {%- capture arrow_up -%}{%- include 'icon-arrow-up' -%}{%- endcapture -%}
          {%- capture arrow_down -%}{%- include 'icon-arrow-down' -%}{%- endcapture -%}
          var sliderArrows = {
            left: {{ arrow_left | json }},
            right: {{ arrow_right | json }},
            up: {{ arrow_up | json }},
            down: {{ arrow_down | json }}
          }
        </script>
        {%- endif -%}
        {% if section.settings.positiontab == "left" %}
          <div class=" {% unless settings.productpage == "product-1" %}pl-5 {% endunless %}" >
            <div class="{% unless settings.productpage == "product-1" %}pl-5 {% endunless %}">
              {% if section.settings.enable_tabvertical %}
                {%- include 'product-information-vertical' -%}
              {% else %}
              {%- include 'product-information' -%}
              {% endif %}
            </div>
          </div>
        {% endif %}
      </div>
    </div>
    <div class="col-12 col-md-6 order-1" itemprop="offers" itemscope itemtype="http://schema.org/Offer">
      <div class="product-single__info-wrapper">
        <meta itemprop="priceCurrency" content="{{ shop.currency }}">
        <link itemprop="availability" href="http://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}">
        <div class="product-single__meta small--text-center">
          <h1 itemprop="name" class="product-single__title">{{ product.title }}<span id="ProductSaleTag-{{ section.id }}" class="{% unless product.compare_at_price > product.price %}hide{% endunless %}">
            <span class="product-tag gradient-theme">
              {{ 'products.product.on_sale' | t }}
            </span>
          </span>
          </h1>
          
          <ul class="pb-3 product-single__meta-list list--inline{% if shop.taxes_included or shop.shipping_policy.body != blank %} product-single__price-container{% endif %}">
            <li>
              {%- unless product.compare_at_price_max > product.price -%}
              <span class="visually-hidden">{{ 'products.product.regular_price' | t }}</span>
              {%- endunless -%}
              <span id="ProductPrice-{{ section.id }}" class="product-single__price" itemprop="price" content="{{ current_variant.price | divided_by: 100.00 }}">
                {{ current_variant.price | money }}
              </span>
            </li>
            {% if product.compare_at_price_max > product.price %}
            <li>
              <span class="visually-hidden">{{ 'products.product.regular_price' | t }}</span>
              <s id="ComparePrice-{{ section.id }}" class="product-single__price product-single__price--compare">
                {{ current_variant.compare_at_price | money }}
              </s>
            </li>
            {% endif %}
            
            {%- if section.settings.stock_enable -%}
            <li>{%- include 'productStock' -%}</li>
            {%- endif -%}
          </ul>
          <div class="review">
            <span class="shopify-product-reviews-badge" data-id="{{ product.id }}"></span>
          </div>
          {% if product.metafields.info.shortdesc != blank %}
          <div class=" pb-4 rte product-single__description" itemprop="Beschreibung">
            {{product.metafields.info.shortdesc}}
          </div>
          {% endif %}
         
        </div>
        
        {% if product.metafields.info.affiliate_link != blank %}{% comment %}AFFILIATE PRODUCT{% endcomment %}
          <a href="{{product.metafields.info.affiliate_link}}" class="my-3 btn btn-theme btn--full product-form__cart-submit {% if section.settings.enable_payment_button %}product-form__cart-submit--outline{% endif %}">
            {{product.metafields.info.affiliate_button}}
          </a>
        {% else %} {% comment %}NORMAL PRODUCT{% endcomment %}
          {% capture "form_class" %}product-form {% if section.settings.product_selector == 'select'%} product-form-select{% endif %} {% if section.settings.enable_payment_button %} product-form--payment-button{% endif %}{%- endcapture %}
          {% capture "form_id" %}AddToCartForm-{{ section.id }}{%- endcapture %}
          {% form 'product', product, class:form_class, id:form_id %}
          {% unless product.has_only_default_variant %}
          {% for option in product.options_with_values %}
          <div class="selector-wrapper js product-form__item">
            <label {% if option.name == 'default' %}class="label--hidden" {% endif %}for="SingleOptionSelector-{{ section.id }}-{{ forloop.index0 }}">{{ option.name }}</label>
            {% include 'productOption' %}
          </div>
          {% endfor %}
          {% endunless %}
          <select name="id" id="ProductSelect-{{ section.id }}" class="product-form__variants no-js">
            {% for variant in product.variants %}
            <option {% if variant == current_variant %} selected="selected" {% endif %} data-sku="{{ variant.sku }}" value="{{ variant.id }}" {% unless variant.available %} disabled="disabled" {% endunless %}>
              {% if variant.available %}
              {{ variant.title }} - {{ variant.price | money_with_currency }}
              {% else %}
              {{ variant.title }} - {{ 'products.product.sold_out' | t }}
              {% endif %}
            </option>
            {% endfor %}
          </select>
        

        
          <div class="product-form__quantity-submit pt-4">
           
            <div class="product-form__item product-form__item--submit {% unless section.settings.enable_payment_button %} btn-disablebuynow {% endunless %}">
              <button type="submit"
                      name="add"
                      id="AddToCart-{{ section.id }}"
                      class="btn btn--full btn-theme product-form__cart-submit {% if section.settings.enable_payment_button %}product-form__cart-submit--outline{% endif %}{% unless current_variant.available %} btn--sold-out{% endunless %}"
                      {% unless current_variant.available %}disabled="disabled"{% endunless %}>
                <span id="AddToCartText-{{ section.id }}">
                  {% unless current_variant.available %}
                  {{ 'products.product.sold_out' | t }}
                  {% else %}
                  {{ 'products.product.add_to_cart' | t }}
                  {% endunless %}
                </span>
              </button>
            </div>
            {% if section.settings.enable_payment_button %}
              <div class="product-form__buynow">
              {{ form | payment_button }}
              {% if current_variant.available == false %}<style>.shopify-payment-button{display:none}</style>{% endif %}
              </div>
            {% endif %}
        </div>
        
          <div class="js-contact-soldout {% if current_variant.available %}hide{% endif %}">
            <span class="btn btn-waitlist btn-theme" data-toggle="modal" data-target="#jsSoldout" title="{{ 'products.product.soldout_messenger' | t }}">{{'products.product.waitlist' | t}}</span>
          </div>
          <div class="gr-btnjs d-flex py-4 align-items-center justify-content-between">
            <div class="d-flex align-items-center">
              <div>
                {% if settings.enable_compare %}
                <span class="btn js-btn-compare mr-4" data-tooltip="true" title="{{ 'products.product.compare_text' | t }}" data-handle="{{product.handle}}">
                  {%- include 'icon-exchange' -%}
                  {%- include 'icon-close' -%}
                  <span>{{ 'products.product.compare_text' | t }}</span>
                </span>
                {% endif %}
                {% if settings.enable_wishlsit %}
                <span data-tooltip="true" class="js-btn-wishlist mr-4" title="{{ 'products.product.wishlist_text' | t }}" data-handle="{{product.handle}}">
                  {%- include 'icon-heart' -%}
                  <span class="wishlist-text">{{ 'products.product.wishlist_text' | t }}</span>
                </span>
                {% endif %}
                  </div>
              <div>
                {%- include 'productSizeGuide' -%}
              </div>
            </div>
            <div>
              {% if section.settings.social_sharing_products %}
                {% include 'social-sharing', share_title: product.title, share_permalink: product.url, share_image: product %}
              {% endif %}
            </div>
          </div> 
          {% endform %}
          {% include 'notifySoldoutProduct' %}
        {% endif %}


        <div class="row text-left pt-2">
          <div class="product-single__information col text-uppercase">
            {% if section.settings.product_vendor_enable %}
            <p class="product-single__vendor"><small class="text-body">{{'products.product.vendor' | t }}: </small><small>{{ product.vendor }}</small></p>
            {% endif %}
            {% if section.settings.product_type_enable %}
            <p class="product-single__type"><small class="text-body">{{'products.product.type' | t }}: </small><small>{{ product.type }}</small></p>
            {% endif %}
            {% if section.settings.variant_sku_enable %}
            <p class="product-single__sku "><small class="text-body">{{'products.product.sku' | t }}: </small><small class="js-variant-sku">{{ current_variant.sku | default:'null' }}</small></p>
            {% endif %}
            {% if section.settings.variant_available_enable %}
            <p class="product-single__availability "><small class="text-body">{{'products.product.available' | t }}: </small><small>{% if current_variant.available %}{{ 'products.product.available' | t }}{% else %}{{'products.product.sold_out' | t }}{% endif %}</small></p>
            {% endif %}
          </div>
          {% if settings.safe_checkout_detail != blank %}
          <div class="safe-checkout-detail col">
            <img class="lazyload img-fluid w-100" data-src="{{ settings.safe_checkout_detail | img_url: '600x' }}"/>
          </div>
          {% endif %}
        </div>
        {% include 'fake-viewer' %}
        {% include 'free-shipping' %}
        {% include 'shipping-time' %}
        {% if settings.enable_countdown_page %}
        {% include 'product-countdown' %}
        {% endif %}
        
        {% if section.settings.positiontab == "right" %}
          {% if section.settings.enable_tabvertical %}
            {%- include 'product-information-vertical' -%}
          {% else %}
          {%- include 'product-information' -%}
          {% endif %}
        {% endif%}
      </div>
    </div>
  </div>
  • Unfortunately this question is too broad to give you a full answer but here are the things I have observed. First your font swapping needs sorting. When the site fonts load in that causes the first layout shift. If you google "CLS font swapping" that will lead you to some tutorials on that. Then the next big problem is the description right below the price loads in via AJAX after page load. This is where a lot of your CLS score is coming from as it pushed all the other content on the page downwards. The answer would be to either not load it via AJAX (make it part of the HTML) or...(cont) – GrahamTheDev Nov 23 '21 at 13:55
  • ...to allocate space for the text by giving the description container (`.itk-product-page-container`) a fixed height, that way when the content loads in the items below are not pushed downwards. Thirdly there is something very wrong with one of your scripts so this section I just mentioned is constantly being fetched and painted. More than likely you accidentally used `setInterval` (fires at regular intervals) instead of `setTimeout` (a one off delay). This certainly won't be helping your performance and could cause paints (rendering items on the page) to register as layout shifts...(cont) – GrahamTheDev Nov 23 '21 at 13:57
  • ...unfortunately this is a complex topic that requires knowledge of terms such as "[critical CSS](https://www.smashingmagazine.com/2015/08/understanding-critical-css/)" (critical CSS is the key to fixing most layout shifts.) https://stackoverflow.com/a/63021718/2702894 may offer some useful tips on how to find layout shifts, https://web.dev/cls explains what CLS is and offers some advice on remediation etc. Hope that at least helps point you in the right direction. – GrahamTheDev Nov 23 '21 at 14:01
  • Dear Graham thank you for your long answer! im confused how the page is still loading fonts since im using system Fonts but ill take a nother look into that! i have now disabeld the Box and still have a high CLS i have also witched the SetInterval to a timeout. I feel like it helped page Performance but has even raised CLS on Desktoppages to 1,9 – Nedjeru Nov 23 '21 at 15:55
  • So now the image loading in causes the layout shift so you need to add a `width` and `height` to that. This is what I was saying about it being big and complicated, unfortunately you have a lot of reading to do as you will fix one thing and then come across another until suddenly everything works and you have 0 CLS. Hopefully this answer wioll give you enough of an understanding of images and CLS -> https://stackoverflow.com/a/65077326/2702894. It is all worth the effort though so keep going and keep reading! Last tip would be to run a performance trace in Dev Tools as that will highlight CLS. – GrahamTheDev Nov 23 '21 at 16:18
  • 1
    thanks for keeping at it Graham. width and height added to the two img. seems like they were not the problem. CLS unchanged. ill now try to dive into critical CSS unless i get other hints. :-) – Nedjeru Nov 23 '21 at 17:23
  • Keep going, when you get down to a specific problem ask another question and we will help however we can. Have a fun time reading about critical CSS, once you get that you will find that CLS issues are much easier to solve as you know what to look for! – GrahamTheDev Nov 23 '21 at 17:31

0 Answers0