Blog Articles
the blog image
Control Your Shopify Upsells: Hiding Products from Recommendations

Hiding Products from Recommendations on Your Shopify Store

As e-commerce store owners, we're always seeking ways to enhance the customer experience. Product recommendations are powerful tools for increasing sales, improving SEO, and reducing cart abandonment. However, there are times when you might want to hide specific products from the "You May Also Like" or recommendation sections of your Shopify store.

Why Hide Products from Recommendations?

There are several reasons to exclude certain products from appearing in recommendations:

  • Seasonal Items

Displaying winter coats during the summer isn't ideal. Hiding seasonal items ensures a relevant shopping experience, showcasing products that match the current season.

  • Out-of-Stock Products

Recommending items that are out of stock can lead to customer frustration. By hiding unavailable products, you prevent disappointment and maintain trust with your shoppers.

  • Exclusive or VIP Products

You might offer certain products exclusively to a select group of customers. Keeping these items hidden from general recommendations maintains their exclusivity and caters to your VIP clientele.

  • Strategic Selection

Carefully choosing which products to hide can support your inventory management, seasonal promotions, or specific sales strategies. It allows you to highlight items that align with your current marketing focus.

  • Streamlining Product Catalog

When dealing with discontinued products or exclusive deals, hiding them from search and recommendations ensures a streamlined browsing experience. This focuses customers' attention on relevant and available offerings.

The Solution: Tagging and Liquid Code Modification

If you're using Shopify's Dawn 15 theme, you can control product recommendations by tagging the products you want to hide and modifying your theme's code.

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Step-by-Step Guide

  1. Tag the Product as "Hidden":

    • Open the product page that you want to hide from recommendations in your Shopify admin.

    • In the product details section, add the tag 'hidden'. This tag will be used in the code to identify products to exclude from recommendations.

  2. Edit Theme Code

    • Go to Online Store > Themes > Edit Code.

    • Open the related-products.liquid file.

    • Within the loop that displays product recommendations, update the code to exclude products with the hidden tag.

      {% unless recommendation.tags contains 'hidden' %}
      
      
      {% endunless %}

Conclusion & Important Considerations

By implementing this method, you can effectively control which products appear in your recommendations, leading to a better shopping experience for your customers and potentially increased sales. Before you proceed, keep these points in mind:

  • Theme Variations: The exact location of the code for product recommendations might differ depending on your specific Shopify theme. Always double-check your theme's structure.

  • Empty Spots: Since the hidden product is not displayed and not automatically replaced, an empty spot may appear in the recommendations section. You'll need to consider how to address this.

  • Testing: Always test your changes in a development or staging environment before applying them to your live store to avoid unintended consequences.

 

ByAnne

Wed Feb 19 2025
the blog image
Enhancing Shopify Dawn Theme with Scrolling Collections

Technical Guide & Strategic UX Design

Scrolling collection lists offer a dynamic way to showcase products while improving user engagement. Building on our previous guide for scrolling text banners, this tutorial focuses on implementing horizontal scroll behavior for collections in Shopify’s Dawn theme, optimized for modern eCommerce needs.

Why Scrolling Collections Drive Conversions

  1. 38% Longer Session Times: Stores using horizontal scroll report increased browsing depth compared to static grids.

  2. Mobile-First Design: 72% of users find horizontal swiping more intuitive than pagination on mobile devices.

  3. Visual Storytelling: Curated collection scrolling enables thematic merchandising (e.g., "Summer Essentials" → "Back-to-School Picks").

Strategic Use Cases:

  • New Arrivals Carousel: Auto-scroll latest collections.

  • Category Highlights: Horizontal navigation between product types.

  • Trending Now: Algorithm-driven collection displays.

Technical Implementation

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Step 1: Create Collection Template

In the sections folder, create a new file named collection-list-marquee.liquid:

{{ 'section-collection-list.css' | asset_url | stylesheet_tag }}
{{ 'component-card.css' | asset_url | stylesheet_tag }}
{{ 'component-slider.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;
    }
  }
  
    @media screen and (min-width: 990px) {
  .grid--6-col-desktop .grid__item {
        width: calc(8.33% - var(--grid-desktop-horizontal-spacing)* 11 / 12);
        max-width: calc(8.33% - var(--grid-desktop-horizontal-spacing)* 11 / 12);
    }
  .grid--5-col-desktop .grid__item {
        width: calc(10% - var(--grid-desktop-horizontal-spacing)* 9 / 10);
        max-width: calc(10% - var(--grid-desktop-horizontal-spacing)* 9 / 10);
    }
  .grid--4-col-desktop .grid__item {
        width: calc(12.5% - var(--grid-desktop-horizontal-spacing)* 7 / 8);
        max-width: calc(12.5% - var(--grid-desktop-horizontal-spacing)* 7 / 8);
    }
  .grid--3-col-desktop .grid__item {
        width: calc(16.66% - var(--grid-desktop-horizontal-spacing)* 5 / 6);
        max-width: calc(16.66% - var(--grid-desktop-horizontal-spacing)* 5 / 6);
    }
  }
{%- endstyle -%}

{%- liquid
  assign columns_mobile_int = section.settings.columns_mobile | plus: 0
  assign show_mobile_slider = false
  if section.settings.swipe_on_mobile and section.blocks.size > columns_mobile_int
    assign show_mobile_slider = true
  endif
-%}

<div class="color-{{ section.settings.color_scheme }} gradient">
  <div class="collection-marquee collection-list-wrapper page-width isolate{% if show_mobile_slider %} page-width-desktop{% endif %}{% if section.settings.title == blank %} no-heading{% endif %}{% if section.settings.show_view_all == false or section.blocks.size > collections.size %} no-mobile-link{% endif %} section-{{ section.id }}-padding">
    {%- unless section.settings.title == blank -%}
      <div class="title-wrapper-with-link{% if show_mobile_slider %} title-wrapper--self-padded-tablet-down{% else %} title-wrapper--self-padded-mobile{% endif %} title-wrapper--no-top-margin">
        <h2
          id="SectionHeading-{{ section.id }}"
          class="collection-list-title inline-richtext {{ section.settings.heading_size }}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}"
        >
          {{ section.settings.title }}
        </h2>

        {%- if section.settings.show_view_all and show_mobile_slider -%}
          <a
            href="{{ routes.collections_url }}"
            id="ViewAll-{{ section.id }}"
            class="link underlined-link large-up-hide{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}"
            aria-labelledby="ViewAll-{{ section.id }} SectionHeading-{{ section.id }}"
          >
            {{- 'sections.collection_list.view_all' | t -}}
          </a>
        {%- endif -%}
      </div>
    {%- endunless -%}

    <slider-component class="slider-mobile-gutter{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}">
      <ul
        class="collection-list contains-card contains-card--collection{% if settings.card_style == 'standard' %} contains-card--standard{% endif %} grid grid--{{ section.settings.columns_desktop }}-col-desktop grid--{{ section.settings.columns_mobile }}-col-tablet-down{% if section.settings.swipe_on_mobile %} slider slider--tablet grid--peek{% endif %} collection-list--{{ section.blocks.size }}-items"
        id="Slider-{{ section.id }}"
        role="list"
      >
        {%- liquid
          assign columns = section.blocks.size
          if columns > 3
            assign columns = 3
          endif
        -%}
        {%- for block in section.blocks -%}
          <li
            id="Slide-{{ section.id }}-{{ forloop.index }}"
            class="collection-list__item grid__item{% if show_mobile_slider %} slider__slide{% endif %}{% if block.settings.collection.featured_image == nil %} collection-list__item--no-media{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}"
            {{ block.shopify_attributes }}
            {% if settings.animations_reveal_on_scroll %}
              data-cascade
              style="--animation-order: {{ forloop.index }};"
            {% endif %}
          >
            {%- assign placeholder_image_index = forloop.index0 | modulo: 4 | plus: 1 -%}
            {%- assign placeholder_image = 'collection-apparel-' | append: placeholder_image_index -%}
            {% render 'card-collection',
              card_collection: block.settings.collection,
              media_aspect_ratio: section.settings.image_ratio,
              columns: columns,
              placeholder_image: placeholder_image
            %}
          </li>
        {%- endfor -%}
                {%- for block in section.blocks -%}
          <li
            id="Slide-{{ section.id }}-{{ forloop.index }}-1"
            class="collection-list__item desktop-only grid__item{% if show_mobile_slider %} slider__slide{% endif %}{% if block.settings.collection.featured_image == nil %} collection-list__item--no-media{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}"
            {{ block.shopify_attributes }}
            {% if settings.animations_reveal_on_scroll %}
              data-cascade
              style="--animation-order: {{ forloop.index }};"
            {% endif %}
          >
            {%- assign placeholder_image_index = forloop.index0 | modulo: 4 | plus: 1 -%}
            {%- assign placeholder_image = 'collection-apparel-' | append: placeholder_image_index -%}
            {% render 'card-collection',
              card_collection: block.settings.collection,
              media_aspect_ratio: section.settings.image_ratio,
              columns: columns,
              placeholder_image: placeholder_image
            %}
          </li>
        {%- endfor -%}
      </ul>
      {%- if show_mobile_slider -%}
        <div class="slider-buttons">
          <button
            type="button"
            class="slider-button slider-button--prev"
            name="previous"
            aria-label="{{ 'general.slider.previous_slide' | t }}"
          >
           {{- 'icon-caret.svg' | inline_asset_content -}}
          </button>
          <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">{{ section.blocks.size }}</span>
          </div>
          <button
            type="button"
            class="slider-button slider-button--next"
            name="next"
            aria-label="{{ 'general.slider.next_slide' | t }}"
          >
            {{- 'icon-caret.svg' | inline_asset_content -}}
          </button>
        </div>
      {%- endif -%}
    </slider-component>

    {%- if section.settings.show_view_all and section.blocks.size < collections.size -%}
      <div
        class="center collection-list-view-all{% if show_mobile_slider %} small-hide medium-hide{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}"
        {% if settings.animations_reveal_on_scroll %}
          data-cascade
        {% endif %}
      >
        <a
          href="{{ routes.collections_url }}"
          class="button"
          id="ViewAllButton-{{ section.id }}"
          aria-labelledby="ViewAllButton-{{ section.id }} SectionHeading-{{ section.id }}"
        >
          {{- 'sections.collection_list.view_all' | t -}}
        </a>
      </div>
    {%- endif -%}
  </div>
</div>

{% schema %}
{
  "name": "Collection list marqueen",
  "tag": "section",
  "class": "section section-collection-list",
  "max_blocks": 15,
  "disabled_on": {
    "groups": ["header", "footer"]
  },
  "settings": [
    {
      "type": "inline_richtext",
      "id": "title",
      "default": "t:sections.collection-list.settings.title.default",
      "label": "t:sections.collection-list.settings.title.label"
    },
    {
      "type": "select",
      "id": "heading_size",
      "options": [
        {
          "value": "h2",
          "label": "t:sections.all.heading_size.options__1.label"
        },
        {
          "value": "h1",
          "label": "t:sections.all.heading_size.options__2.label"
        },
        {
          "value": "h0",
          "label": "t:sections.all.heading_size.options__3.label"
        },
        {
          "value": "hxl",
          "label": "t:sections.all.heading_size.options__4.label"
        },
        {
          "value": "hxxl",
          "label": "t:sections.all.heading_size.options__5.label"
        }
      ],
      "default": "h1",
      "label": "t:sections.all.heading_size.label"
    },
    {
      "type": "select",
      "id": "image_ratio",
      "options": [
        {
          "value": "adapt",
          "label": "t:sections.collection-list.settings.image_ratio.options__1.label"
        },
        {
          "value": "portrait",
          "label": "t:sections.collection-list.settings.image_ratio.options__2.label"
        },
        {
          "value": "square",
          "label": "t:sections.collection-list.settings.image_ratio.options__3.label"
        }
      ],
      "default": "square",
      "label": "t:sections.collection-list.settings.image_ratio.label",
      "info": "t:sections.collection-list.settings.image_ratio.info"
    },
    {
      "type": "range",
      "id": "columns_desktop",
      "min": 1,
      "max": 6,
      "step": 1,
      "default": 3,
      "label": "t:sections.collection-list.settings.columns_desktop.label"
    },
    {
      "type": "color_scheme",
      "id": "color_scheme",
      "label": "t:sections.all.colors.label",
      "info": "t:sections.all.colors.has_cards_info",
      "default": "scheme-1"
    },
    {
      "type": "checkbox",
      "id": "show_view_all",
      "default": false,
      "label": "t:sections.collection-list.settings.show_view_all.label"
    },
    {
      "type": "header",
      "content": "t:sections.collection-list.settings.header_mobile.content"
    },
    {
      "type": "select",
      "id": "columns_mobile",
      "options": [
        {
          "value": "1",
          "label": "t:sections.collection-list.settings.columns_mobile.options__1.label"
        },
        {
          "value": "2",
          "label": "t:sections.collection-list.settings.columns_mobile.options__2.label"
        }
      ],
      "default": "1",
      "label": "t:sections.collection-list.settings.columns_mobile.label"
    },
    {
      "type": "checkbox",
      "id": "swipe_on_mobile",
      "default": false,
      "label": "t:sections.collection-list.settings.swipe_on_mobile.label"
    },
    {
      "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
    }
  ],
  "blocks": [
    {
      "type": "featured_collection",
      "name": "t:sections.collection-list.blocks.featured_collection.name",
      "settings": [
        {
          "type": "collection",
          "id": "collection",
          "label": "t:sections.collection-list.blocks.featured_collection.settings.collection.label"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "t:sections.collection-list.presets.name",
      "blocks": [
        {
          "type": "featured_collection"
        },
        {
          "type": "featured_collection"
        },
        {
          "type": "featured_collection"
        }
      ]
    }
  ]
}
{% endschema %}

<style>
  .desktop-only {
    display: none;
  }
@media screen and (min-width: 990px) {
    .collection-marquee .slider-mobile-gutter {
    overflow:hidden;
  }
  .collection-marqueer .slider-mobile-gutter ul {
    animation: marquee 18s linear infinite;
    width: 200%;
  }
  .collection-marquee .slider-mobile-gutter ul li {
    float: left;
  }
  .desktop-only {
    display: block;
  }
  @keyframes marquee {
			from {
				transform: translateX( 0% );
			}
			to {
				transform: translateX( -50% );
			}
  }
}

</style>

Step 2: Add to Theme

  1. In Shopify Admin:

    1. Navigate to Online Store > Themes > Customize.

    2. Add the "Collection list marquee" section to your homepage or anywhere you wish to place it.

Note: This currently supports only 3 to 6 collections.

Conclusion

By integrating scrolling collections into the Dawn theme, eCommerce stores can showcase a greater variety of products and encourage deeper engagement. Following the outlined steps allows merchants to create a dynamic and visually appealing storefront that enhances product discovery.

BySonya

Thu Feb 06 2025
the blog image
Enhancing Shopify Dawn Theme with Scrolling Text

Technical Guide & Strategic Implementation

Scrolling text marquees have become a powerful tool for eCommerce stores to communicate urgent messages and boost conversions. This technical guide demonstrates how to implement a scrolling text feature in Shopify’s Dawn theme while exploring strategic use cases and optimization tactics.

Why Scrolling Text Matters in eCommerce

  1. Potential Conversion Lift: Stores using scrolling marquees have reported increases in conversion rates by highlighting time-sensitive offers, though the exact impact can vary.

  2. Mobile Optimization: With 65% of Shopify traffic from mobile devices, compact scrolling text efficiently utilizes limited screen space.

  3. Attention Retention: Animated elements capture 3x more visual attention than static text.

Common business scenarios include:

  • Flash Sales: "24-HOUR SALE: 50% OFF ALL OUTERWEAR – USE CODE HURRICANE"

  • Shipping Alerts: "FREE EXPRESS SHIPPING FOR ORDERS ABOVE $100"

  • New Launches: "JUST IN: SUMMER 2025 COLLECTION – SHOP NOW"

  • Trust Badges: "⭐ 4.9/5 RATED BY 15K CUSTOMERS | 30-DAY RETURNS"

Implementation Steps

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Step 1: Create Liquid Template

In your theme code editor, create a new file named my-marquee.liquid in the sections folder:

<link rel="stylesheet" href="{{ 'section-scrolling-text.min.css' | asset_url }}" media="all">
{%- assign get = section.settings -%}
{% style %}
    .ccpro-scrolling-text#section_{{ section.id }} {
        background: {{ get.background_color }};
        color: {{ get.text_color }};
        padding-top: {{ get.spacing_top_mobile }}px;
        padding-bottom: {{ get.spacing_bottom_mobile }}px;
        --text-font-weight: {{ get.text_font_weight }};
        font-size: {{ get.text_size_mobile }}px;
        --marquee-speed: {{ get.speed_mobile }}s;
        --icon-size: {{ get.icon_size_mobile }}px;
    }

    @media (min-width:992px) {
        .ccpro-scrolling-text#section_{{ section.id }} {
            padding-top: {{ get.spacing_top_desktop }}px;
            padding-bottom: {{ get.spacing_bottom_desktop }}px;
            font-size: {{ get.text_size_desktop }}px;
            --marquee-speed: {{ get.speed_desktop }}s;
            --icon-size: {{ get.icon_size_desktop }}px;
        }
    }


{% endstyle %}

<div class="ccpro-scrolling-text" id="section_{{ section.id }}">
    <div class="{% if get.enable_container %}page-width{% endif %}">
        <div class="ccpro-st__inner ccpro-st__direction-{{ get.direction }}">

            {% for i in (1..5) %}
            <div>
                {%- for block in section.blocks -%}
                    {%- assign get = block.settings -%}
                    {%- if get.text != blank or get.selected_icon != blank -%}
                        <div class="ccpro-st__item" {{ block.shopify_attributes }}>

                            {%- if get.selected_icon != blank -%}
                            <div class="ccpro-st__item-icon">
                                <div class="ccpro-aspect-ratio" style="padding-top:{{ 1 | divided_by: get.selected_icon.aspect_ratio | times: 30 }}%">
                                  {{ get.selected_icon | image_url: width: 18, height:18 | image_tag  }}
                                </div>
                            </div>
                            {%- endif -%}

                            {%- if get.text != blank -%}
                            <span class="ccpro-st__item-text">{{ get.text }}</span>
                            {%- endif -%}

                        </div>
                        {%- endif -%}
                {%- endfor -%}
            </div>
            {% endfor %}

        </div>
    </div>
</div>

{% schema %}
{
  "name": "Scrolling Announcement Bar",
  "settings": [
    {
        "type": "paragraph",
        "content": "Subscribe to our channel [Shinetech ChangChun](https:\/\/youtube.com\/@ShinetechShopidev?sub_confirmation=1)"
    },
    {
        "type": "color",
        "id": "background_color",
        "label": "Background Color",
        "default": "#fff"
    },
    {
        "type": "color",
        "id": "text_color",
        "label": "Text Color",
        "default": "#000"
    },
    {
        "type": "checkbox",
        "id": "enable_container",
        "label": "Enable Container",
        "default": true
    },
    {
        "type": "header",
        "content": "Texts"
    },
    {
        "type": "range",
        "id": "text_font_weight",
        "label": "Font Weight",
        "min": 100,
        "max": 900,
        "step": 100,
        "default": 500
    },
    {
        "type": "range",
        "id": "text_size_desktop",
        "label": "Text Size (Desktop)",
        "unit": "px",
        "min": 10,
        "max": 200,
        "step": 2,
        "default": 48
    },
    {
        "type": "range",
        "id": "text_size_mobile",
        "label": "Text Size (Mobile)",
        "unit": "px",
        "min": 8,
        "max": 200,
        "step": 2,
        "default": 32
    },
    {
        "type": "header",
        "content": "Icon/Logo Size"
    },
    {
        "type": "range",
        "id": "icon_size_desktop",
        "label": "Size (Desktop)",
        "unit": "px",
        "min": 0,
        "max": 200,
        "step": 2,
        "default": 36
    },
    {
        "type": "range",
        "id": "icon_size_mobile",
        "label": "Size (Mobile)",
        "unit": "px",
        "min": 0,
        "max": 100,
        "step": 1,
        "default": 25
    },
    {
        "type": "select",
        "id": "direction",
        "label": "Direction",
        "options": [
            {
                "value": "left",
                "label": "Left"
            },
            {
                "value": "right",
                "label": "Right"
            }
        ]
    },
    {
        "type": "range",
        "id": "speed_desktop",
        "label": "Speed (Desktop)",
        "unit": "s",
        "min": 1,
        "max": 100,
        "step": 1,
        "default": 10
    },
    {
        "type": "range",
        "id": "speed_mobile",
        "label": "Speed (Mobile)",
        "unit": "s",
        "min": 1,
        "max": 100,
        "step": 1,
        "default": 10
    },
    {
        "type": "header",
        "content": "Spacing"
    },
    {
        "type": "range",
        "id": "spacing_top_desktop",
        "label": "Spacing Top (Desktop)",
        "unit": "px",
        "min": 0,
        "max": 200,
        "step": 5,
        "default": 10
    },
    {
        "type": "range",
        "id": "spacing_bottom_desktop",
        "label": "Spacing Bottom (Desktop)",
        "unit": "px",
        "min": 0,
        "max": 200,
        "step": 5,
        "default": 10
    },
    {
        "type": "range",
        "id": "spacing_top_mobile",
        "label": "Spacing Top (Mobile)",
        "unit": "px",
        "min": 0,
        "max": 200,
        "step": 5,
        "default": 10
    },
    {
        "type": "range",
        "id": "spacing_bottom_mobile",
        "label": "Spacing Bottom (Mobile)",
        "unit": "px",
        "min": 0,
        "max": 200,
        "step": 5,
        "default": 10
    }
  ],
  "blocks": [
    {
        "type": "text",
        "name": "Text",
        "settings": [
            {
                "type": "image_picker",
                "id": "selected_icon",
                "label": "Select Icon",
                "info": "Recommended to have consistent Icon/Logo dimension"
            },
            {
                "type": "inline_richtext",
                "id": "text",
                "label": "Content",
                "default": "Add campaign text here."
            }
        ]
    }
  ],
  "presets": [
    {
      "name": "Scrolling Annoucement Bar",
      "blocks": [
        {
            "type": "text",
            "settings": {
                "text": "Add your text here."
            }
        },
        {
            "type": "text",
            "settings": {
                "text": "Add your text here."
            }
        },
        {
            "type": "text",
            "settings": {
                "text": "Add your text here."
            }
        }
      ]
    }
  ]
}
{% endschema %}

Step 2: Add CSS Animation

Create a new file named section-scrolling-text.min.css in the assets folder:

.ccpro-scrolling-text .ccpro-st__inner{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;white-space:nowrap;font-size:inherit;font-weight:bold;overflow:hidden;position:relative;z-index:2}
.ccpro-scrolling-text .ccpro-st__inner>div{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-flex-shrink:0;flex-shrink:0;-webkit-animation:marqueeLeft var(--marquee-speed) linear infinite;
                                         animation:marqueeLeft var(--marquee-speed) linear infinite}
.ccpro-scrolling-text .ccpro-st__inner.ccpro-st__direction-right>div{-webkit-animation:marqueeRight var(--marquee-speed) linear infinite;animation:marqueeRight var(--marquee-speed) linear infinite}
.ccpro-scrolling-text .ccpro-st__item{pointer-events:none;display:-webkit-inline-flex;display:inline-flex;-webkit-align-items:center;align-items:center;padding:0 30px}
.ccpro-scrolling-text .ccpro-st__item span{transition:all .25s cubic-bezier(0.104,0.204,0.492,1)}.ccpro-scrolling-text .ccpro-st__item-icon{width:var(--icon-size);margin-right:10px}
.ccpro-scrolling-text .ccpro-st__item-text{font-weight:var(--text-font-weight)}.ccpro-scrolling-text .ccpro-st__item-text a{color:inherit}

    @keyframes marqueeLeft {
    from {
      -webkit-transform:translateX(0);
      transform: translateX(0);
    }
    to {
      -webkit-transform:translateX(-100%);
      transform: translateX(-100%);
    }
  }

      @keyframes marqueeRight {
    from {
      -webkit-transform:translateX(-100%);
      transform: translateX(-100%);
    }
    to {
       -webkit-transform:translateX(0);
      transform: translateX(0);
    }
  }

Step 3: Enable in Theme Editor

  1. In Shopify Admin, navigate to Online Store > Themes > Customize.

  2. Add your new "Custom Marquee" section to the header or product template.

Strategic Optimization Tips

  1. Speed & Readability:

    • Optimal duration: 15-25 seconds per cycle.

    • Contrast ratio ≥ 4.5:1 for accessibility.

  2. Content Best Practices:

    • Keep messages under 120 characters.

    • Use emojis sparingly: 🚚 Free Shipping | 🔥 50% Off

Final Thoughts

By integrating scrolling text into the Dawn theme, eCommerce stores can effectively communicate critical messages and drive higher engagement. Following the steps in this guide enables merchants to create urgency and improve customer interaction, similar to successful strategies employed by top industry players. For those who prefer a more straightforward approach, public apps in the Shopify app store offer easy-to-use alternatives that still align with brand aesthetics and functionality.

BySonya

Thu Feb 06 2025
the blog image
How to Add a Custom Message to Your Packing Slip Template

Background

Adding a custom message to your packing slip template can significantly enhance your customer's shopping experience. This feature allows your customers to leave a note for a product specifically, sharing their requirements with you. This is especially helpful if you provide customization services such as engraving on a product, for example, on a pen or a box. Or it can be used to include gift messages, special handling instructions, or dietary preferences if you're selling consumable goods. The idea originated from a Shopify community post where users discussed ways to show their order note on the packing slips to better satisfy the customers.

Step-by-Step Guide

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

  1. Add the Custom Message Field to Your Product Page:

    • Open your product page's code, typically found in main-product.liquid.

    • Insert the following code snippet to add a custom message field:

                        <p class="line-item-property__field">
                          <label for="message">Message</label>
                          <input id="message" type="text" name="properties[message]"  form="{{ product_form_id }}">
                        </p>
  2. Edit Your Packing Slip Template:

    • Go to your Shopify admin panel.

    • Navigate to Settings > Shipping and Delivery.

    • Click on Edit Packing Slip Template.

    • Paste the following code into the template:

                  {%- for property in line_item.properties -%}
                    {%- assign property_first_char = property.first | slice: 0 -%}
                    {%- if property.last != blank and property_first_char != '_' -%}
                      <span class="line-item-description-line">
                        {{ property.first }}:{{ property.last }}
                      </span>
                    {%- break -%}
                    {%- endif -%}
                  {%- endfor -%}

By following these steps, you can successfully add a custom message to your packing slip template, enhancing your customer's experience and reinforcing your brand identity.

Additional Examples:

  • Gift Messages: Allow customers to add personalized notes when sending gifts.

  • Special Handling Instructions: Useful for fragile items or specific delivery requirements.

  • Dietary Preferences: For businesses selling food products, allow customers to specify any dietary restrictions or preferences.

Implementing this feature can increase customer satisfaction and loyalty by providing a more personalized shopping experience. Happy coding!

BySonya

Sun Jan 26 2025
the blog image
Adding Universally Editable Text in Shopify

Personalizing your Shopify store can significantly impact customer engagement and user experience. Store owners may also want to display a piece of text in multiple places on the website and be able to edit it easily together. This guide will show you how to add a custom field to your theme settings, make it editable via the Shopify theme editor, and display the custom text on your pages—all without hard-coding changes that would be difficult to update later.

Why It's Needed

Customizing the content on your Shopify store’s pages can serve many purposes. Whether you want to give personalized instructions, provide welcoming messages, or include additional information, being able to edit this text directly from the theme editor all at once makes it much easier to manage. This flexibility is particularly useful for store owners who may not be comfortable with coding but still want to maintain control over their site’s content.

In our example below, we will add a piece of text to the login page of Shopify. Please note that you need to set your customer account to legacy mode, as the new customer account page does not allow editing.

Steps to Implement

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Step 1: Add the Custom Field to the Theme Settings

First, we need to add a custom field to the theme settings. This field will allow you to edit the login page text from the Shopify admin panel. Here's how to do it:

  1. Navigate to the Online Store: In your Shopify Admin, go to Online Store.

  2. Customize Your Theme: Under Themes, click Customize next to the Dawn theme (or your active theme).

  3. Open Theme Settings: In the theme editor, locate Theme settings at the bottom left.

  4. Check for Existing Options: Look for options related to Accounts or Login. If the field for the text you want to edit doesn't exist, you'll need to create a new custom setting.

To do this, we will edit the settings_schema.json file.

Step 2: Add a Custom Field in settings_schema.json

You need to add a custom field in the settings_schema.json file to make the text editable from the admin panel. Follow these steps:

  1. Edit the Code: In your Shopify Admin, go to Online Store > Themes > Actions > Edit Code.

  2. Locate the File: Open the config/settings_schema.json file.

  3. Add the Custom Setting: Add a new setting for your custom text using the following structure:

{
"name": "Login Page Text",
"settings": [
{
"type": "textarea",
"label": "Custom Text for Login Page",
"id": "login_page_text",
"default": "Enter your credentials and select the 'Sign In' button to access your account.",
"info": "This text will be displayed on the login page."
}
]
}

Step 3: Update the main-login.liquid File to Display the Custom Text

Next, you need to update the main-login.liquid file to display the custom text. Here’s how:

  1. Open the Liquid File: In the theme editor, find and open the main-login.liquid file (or the relevant Liquid file where you want the text to appear).

  2. Insert the Custom Text: Use the following code to pull the custom text from the settings:

<p>
{{ settings.login_page_text }}
</p>

Step 4: Customize the Text in Shopify Admin

Finally, after adding the setting and updating the Liquid file, you will be able to change the text from the Shopify Admin. Here are the steps:

  1. Access the Theme Settings: Go to Online Store > Themes.

  2. Customize the Theme: Click Customize next to your theme.

  3. Find the Custom Text Field: Go to Theme Settings (bottom-left corner) and look for the Custom Text for Login Page field under Accounts or Login settings.

  4. Edit and Save: Edit the text as needed and save your changes.

Now, you can easily modify the login page text directly from the Shopify Admin without needing to dive back into the code each time you want to make a change. Additionally, you can add this piece of text to other places on the site by repeating step 3.

ByEric

Mon Jan 20 2025
the blog image
Optimize Dawn: Image Banners for Every Device

As more people use their phones for online shopping, providing a responsive user experience is more critical than ever. In today’s mobile-first world, optimizing your website for various devices isn't just a bonus—it's a necessity. One key feature that can enhance the user experience on your e-commerce site is displaying different image banners based on the device being used. Different devices have different screen sizes, resolutions, and user interactions. By tailoring your image banners for specific devices, you create a seamless and engaging experience, which can lead to higher engagement rates and increased conversions. This approach keeps your site visually appealing and functional, whether your visitors are using a mobile phone, tablet, or desktop.

In addition, using different image sizes for different screens can improve your site's performance, especially on mobile devices, as smaller images load faster. This not only enhances the overall user experience but also contributes to better site performance.

In this blog, we’ll walk you through the steps to show different image banners on different devices for the Dawn theme in Shopify.

Steps to Add Device-Specific Image Banners

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

  1. Add Two Image Banner Sections to Your Homepage

First, you'll need to add two image banner sections to your homepage. This allows you to create and customize banners specifically designed for desktop and mobile views.

  1. Preview the Theme and Note the Section IDs

Next, preview your theme and make a note of the section IDs. The section IDs are unique identifiers for each section in your theme, and you'll need them to specify which banners should be shown on which devices.

  1. Edit Your theme.liquid File

Now, open your theme.liquid file. This file controls the overall layout and structure of your theme. Scroll to the bottom of the file and add the following code snippet. Ensure you replace your_section_id with the actual section IDs you noted earlier.

    <style>
    @media screen and (min-width: 751px) {
         #shopify-section-template--23119250981154__image_banner {
            display: none
          }     
      }
    @media screen and (max-width: 750px) {
         #shopify-section-template--23119250981154__image_banner_PJ3nfy {
            display: none
          }     
      }

    </style>

Note: It's crucial to replace your_section_id_desktop and your_section_id_mobile with the corresponding section IDs for your desktop and mobile banners.

By following these steps, you can enhance your e-commerce site’s responsiveness and visual appeal, ensuring that your customers enjoy a seamless browsing experience regardless of the device they use. This small tweak can make a big difference in user satisfaction and engagement, ultimately driving more conversions and sales for your online store.

ByAnne

Wed Jan 15 2025
the blog image
Shopify Tutorial: How to Display Only Selected Variant Images

Recently, a Shopify store owner approached us with a specific requirement: they wanted to showcase only the images of the selected variant in their product gallery. This need arose from having multiple variants for a single product, each with its own set of images. By displaying only the relevant images, the store owner aimed to streamline the user experience, allowing customers to focus on the variant they were interested in without distraction.

Benefits of Displaying Only Selected Variant Images

Implementing this feature can significantly enhance your store's functionality and user experience. Here are some key advantages:

  • Improved User Experience: Customers can easily view and compare images of the selected variant without being overwhelmed by irrelevant images.

  • Enhanced Visual Appeal: A cleaner and more organized product gallery makes your store visually appealing, encouraging customers to explore further.

  • Increased Conversion Rates: Simplifying product presentation helps customers make informed purchasing decisions, potentially boosting conversion rates.

Steps to Implement Selected Variant Images in Shopify

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

To display only the images of the selected variant in your Shopify store, follow these straightforward steps:

  1. Access Your Theme Code Editor: Log into your Shopify admin panel and navigate to "Online Store" > "Themes." Click on "Actions" and select "Edit code" for your active theme.

  2. Modify sections/main-product.liquid: Open the sections/main-product.liquid file. Add below code to the file.

    ,
        {
          "type": "header",
          "content": "Selected Variant Images"
        },
        {
            "type": "paragraph",
            "content": "Subscribe our channel [Shinetech ChangChun](https:\/\/youtube.com\/@ShinetechShopidev?sub_confirmation=1)"
        },
        {
          "type": "checkbox",
          "id": "enable_group_media",
          "label": "Enable Selected Variant Images",
          "default": false
        }
  3. Update snippets/product-media-gallery.liquid: Locate and open the snippets/product-media-gallery.liquid file. Replace the existing code with code below.

    
    {% comment %}
      Renders a product media gallery. Should be used with 'media-gallery.js'
      Also see 'product-media-modal'
    
      Accepts:
      - product: {Object} Product liquid object
      - variant_images: {Array} Product images associated with a variant
      - limit: {Number} (optional) When passed, limits the number of media items to render
    
      Usage:
      {% render 'product-media-gallery' %}
    {% endcomment %}
    
    {%- liquid
      if section.settings.hide_variants and variant_images.size == product.media.size
        assign single_media_visible = true
      endif
    
      if limit == 1
        assign single_media_visible = true
      endif
    
      assign media_count = product.media.size
      if section.settings.hide_variants and media_count > 1 and variant_images.size > 0
        assign media_count = media_count | minus: variant_images.size | plus: 1
      endif
    
      if media_count == 1 or single_media_visible
        assign single_media_visible_mobile = true
      endif
    
      if media_count == 0 or single_media_visible_mobile or section.settings.mobile_thumbnails == 'show' or section.settings.mobile_thumbnails == 'columns' and media_count < 3
        assign hide_mobile_slider = true
      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
    -%}
    
    <media-gallery
      id="MediaGallery-{{ section.id }}"
      role="region"
      {% if section.settings.enable_sticky_info %}
        class="product__column-sticky"
      {% endif %}
      aria-label="{{ 'products.product.media.gallery_viewer' | t }}"
      data-desktop-layout="{{ section.settings.gallery_layout }}"
    >
      <div id="GalleryStatus-{{ section.id }}" class="visually-hidden" role="status"></div>
      <slider-component id="GalleryViewer-{{ section.id }}" class="slider-mobile-gutter">
        <a class="skip-to-content-link button visually-hidden quick-add-hidden" href="#ProductInfo-{{ section.id }}">
          {{ 'accessibility.skip_to_product_info' | t }}
        </a>
        <ul
          id="Slider-Gallery-{{ section.id }}"
          class="product__media-list contains-media 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 single_media_visible %} product__media-item--single{% endif %}{% if featured_media.media_type != 'image' %} product__media-item--full{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--fade-in{% endif %}"
              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,
                media_fit: section.settings.media_fit,
                constrain_to_viewport: section.settings.constrain_to_viewport,
                lazy_load: false
              %}
            </li>
          {%- endif -%}
          {%- for media in product.media -%}
            {% if media_position >= limit
              or media_position >= 1
              and section.settings.hide_variants
              and variant_images contains media.src
            %}
              {% continue %}
            {% endif %}
            {%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
                {%- liquid
                  assign media_position = media_position | default: 0 | plus: 1
                  assign lazy_load = false
                  if media_position > 1
                    assign lazy_load = true
                  endif
                  assign current_color = product.selected_or_first_available_variant.option1              
                -%}
                {% if section.settings.enable_group_media == false or media_count == 1 or current_color == media.alt and section.settings.enable_group_media == true  %}
              <li
                id="Slide-{{ section.id }}-{{ media.id }}"
                class="product__media-item grid__item slider__slide{% if single_media_visible %} product__media-item--single{% endif %}{% if product.selected_or_first_available_variant.featured_media == nil and forloop.index == 1 %} is-active{% endif %}{% if media.media_type != 'image' %} product__media-item--full{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--fade-in{% endif %}"
                data-media-id="{{ section.id }}-{{ media.id }}"
              >
                {% 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>
                   {% endif %}
            {%- endunless -%}
          {%- endfor -%}
        </ul>
        <div class="slider-buttons quick-add-hidden{% if hide_mobile_slider %} small-hide{% endif %}">
          <button
            type="button"
            class="slider-button slider-button--prev"
            name="previous"
            aria-label="{{ 'general.slider.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">{{ 'general.slider.of' | t }}</span>
            <span class="slider-counter--total">{{ media_count }}</span>
          </div>
          <button
            type="button"
            class="slider-button slider-button--next"
            name="next"
            aria-label="{{ 'general.slider.next_slide' | t }}"
          >
            {% render 'icon-caret' %}
          </button>
        </div>
      </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 -%}
      {%- liquid
        assign is_not_limited_to_single_item = false
        if limit == null or limit > 1
          assign is_not_limited_to_single_item = true
        endif
      -%}
      {%- if is_not_limited_to_single_item
        and media_count > 1
        and section.settings.gallery_layout contains 'thumbnail'
        or section.settings.mobile_thumbnails == 'show'
      -%}
        <slider-component
          id="GalleryThumbnails-{{ section.id }}"
          class="thumbnail-slider slider-mobile-gutter quick-add-hidden{% unless section.settings.gallery_layout contains 'thumbnail' %} medium-hide large-up-hide{% endunless %}{% if section.settings.mobile_thumbnails != 'show' %} small-hide{% endif %}{% if media_count <= 3 %} thumbnail-slider--no-slide{% endif %}"
        >
          <button
            type="button"
            class="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"
          >
            {% render 'icon-caret' %}
          </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 %}"
          >
            {%- capture 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 - 8rem) / 3)
            {%- endcapture -%}
    
            {%- if featured_media != null -%}
              {%- liquid
                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 }}"
                data-alt="{{ media.alt }}"
              >
                {%- capture thumbnail_id -%}
                  Thumbnail-{{ section.id }}-0
                {%- endcapture -%}
                
                <button
                  class="thumbnail global-media-settings global-media-settings--no-shadow"
                  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_id }}"
                >
                  {{
                    featured_media.preview_image
                    | image_url: width: 416
                    | image_tag:
                      loading: 'lazy',
                      sizes: sizes,
                      widths: '54, 74, 104, 162, 208, 324, 416',
                      id: thumbnail_id,
                      alt: featured_media.alt
                    | escape
                  }}
                </button>
              </li>          
            {%- endif -%}
            {%- for media in product.media -%}
              {%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
                {%- liquid
                  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 }}"
                  data-alt="{{ media.alt }}"
                  data-enable-group-media="{{ section.settings.enable_group_media }}"
                >
                  {%- 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 -%}
                  {%- capture thumbnail_id -%}
                    Thumbnail-{{ section.id }}-{{ forloop.index }}
                  {%- endcapture -%}
                  <button
                    class="thumbnail global-media-settings global-media-settings--no-shadow"
                    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_id }}"
                  >
                    {{
                      media.preview_image
                      | image_url: width: 416
                      | image_tag:
                        loading: 'lazy',
                        sizes: sizes,
                        widths: '54, 74, 104, 162, 208, 324, 416',
                        id: thumbnail_id,
                        alt: media.alt
                      | escape
                    }}
                  </button>
                 
                </li>
              {%- endunless -%}
            {%- endfor -%}
          </ul>
          <button
            type="button"
            class="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"
          >
            {% render 'icon-caret' %}
          </button>
        </slider-component>
      {%- endif -%}
    </media-gallery>
    
  4. Edit snippets/product-media-modal.liquid: Open this file and replace the code with below code.

    {% comment %}
      Renders a product media modal. Also see 'product-media-gallery'
    
      Accepts:
      - product: {Object} Product liquid object
      - variant_images: {Array} Product images associated with a variant
    
      Usage:
      {% render 'product-media-modal' %}
    {% endcomment %}
    
    <product-modal id="ProductModal-{{ section.id }}" class="product-media-modal media-modal">
      <div
        class="product-media-modal__dialog color-{{ section.settings.color_scheme }} gradient"
        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 }}"
        >
          {{ 'icon-close.svg' | inline_asset_content }}
        </button>
    
        <div
          class="product-media-modal__content color-{{ section.settings.color_scheme }} gradient"
          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 or variant_images contains media.id
                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>
    
    
    
  5. Edit snippets/product-variant-picker.liquid: Open this file and replace the code with below code.

    {% comment %}
      Renders product variant-picker
    
      Accepts:
      - product: {Object} product object.
      - block: {Object} passing the block information.
      - product_form_id: {String} Id of the product form to which the variant picker is associated.
      Usage:
      {% render 'product-variant-picker', product: product, block: block, product_form_id: product_form_id %}
    {% endcomment %}
    {%- unless product.has_only_default_variant -%}
      <variant-selects
        id="variant-selects-{{ section.id }}"
        data-section="{{ section.id }}"
        {{ block.shopify_attributes }}
      >
        {%- for option in product.options_with_values -%}
          {%- liquid
            assign swatch_count = option.values | map: 'swatch' | compact | size
            assign picker_type = block.settings.picker_type
    
            if swatch_count > 0 and block.settings.swatch_shape != 'none'
              if block.settings.picker_type == 'dropdown'
                assign picker_type = 'swatch_dropdown'
              else
                assign picker_type = 'swatch'
              endif
            endif
          -%}
          {%- if picker_type == 'swatch' -%}
            <fieldset class="js product-form__input product-form__input--swatch" data-option-name="{{ option.name }}">
              <legend class="form__label">
                {{ option.name }}:
                <span data-selected-value>
                  {{- option.selected_value -}}
                </span>
              </legend>
              {% render 'product-variant-options',
                product: product,
                option: option,
                block: block,
                picker_type: picker_type
              %}
            </fieldset>
          {%- elsif picker_type == 'button' -%}
            <fieldset class="js product-form__input product-form__input--pill" data-option-name="{{ option.name }}">
              <legend class="form__label">{{ option.name }}</legend>
              {% render 'product-variant-options',
                product: product,
                option: option,
                block: block,
                picker_type: picker_type
              %}
            </fieldset>
          {%- else -%}
            <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">
                {%- if picker_type == 'swatch_dropdown' -%}
                  <span
                    data-selected-value
                    class="dropdown-swatch"
                  >
                    {% render 'swatch', swatch: option.selected_value.swatch, shape: block.settings.swatch_shape %}
                  </span>
                {%- endif -%}
                <select
                  id="Option-{{ section.id }}-{{ forloop.index0 }}"
                  class="select__select"
                  name="options[{{ option.name | escape }}]"
                  form="{{ product_form_id }}"
                  data-option-name="{{ option.name }}"
                >
                  {% render 'product-variant-options',
                    product: product,
                    option: option,
                    block: block,
                    picker_type: picker_type
                  %}
                </select>
                <span class="svg-wrapper">
                  {{- 'icon-caret.svg' | inline_asset_content -}}
                </span>
              </div>
            </div>
          {%- endif -%}
        {%- endfor -%}
    
        <script type="application/json" data-selected-variant>
          {{ product.selected_or_first_available_variant | json }}
        </script>
      </variant-selects>
    {%- endunless -%}
  6. Update assets/product-info.js: In this file, add a new function called setVariantImages. Update the file Update the file with reference to the screenshots below.

    Update line 17 :

    this.setVariantImages()

    Update line 308 and 315:

    this.setVariantImages()
     setVariantImages() {
           // set variant images in the media gallery
            const galleryImages = Array.from(document.querySelectorAll('[data-alt]'));
            const variantOptionNames = Array.from(document.querySelectorAll('[data-option-name]'));
            let setVariantImages = false;
            let value;
            variantOptionNames.forEach((variantOption)=> {
              if(variantOption.getAttribute('data-option-name').toLowerCase() == 'color') {
                var inputs = variantOption.querySelectorAll('input');
                if (inputs.length)
                {
                  value = Array.from(variantOption.querySelectorAll('input')).find((radio) => radio.checked).value;
                } else {
                  value = variantOption.value;
                }
                
                setVariantImages = true;
              }
            })
            if(setVariantImages) {
              galleryImages.forEach((galleryImage) => {
                if((galleryImage.getAttribute('data-alt') == value || galleryImage.getAttribute('data-alt').toLowerCase() == 'public' ) && galleryImage.getAttribute('data-enable-group-media') == 'true') {
                  galleryImage.classList.remove("thumbnail-list_item--variant");            
                } else {
                  galleryImage.classList.add("thumbnail-list_item--variant");
                }          
              })
           }
          }
  7. Set Up Variant Image Alt Text: In your Shopify admin, ensure that each variant image has alt text that exactly matches its corresponding variant option name. Also, make sure that the the variant name is 'color'. If you have images that need to be used for all variants, please set the alt text as 'public'.

By following these steps, you can effectively display only the images of the selected variant in your product gallery. This enhancement will not only improve user experience but also elevate the overall visual appeal of your Shopify store.

BySonya

Tue Dec 24 2024
the blog image
Optimize Your Shopify Footer: Integrate Newsletter Subscription with Footer in Dawn 15

Business Scenario

A common request from Shopify store owners is to enhance the footer area for better user engagement and improved aesthetic. One effective way to achieve this is by placing the newsletter subscription box in the same column as the menu links. This streamlined approach not only saves space but also encourages visitors to subscribe while browsing the footer menu.

Benefits of a Unified Footer Layout

Implementing this feature offers several advantages:

  • Space Optimization: Combining the newsletter subscription box with the menu saves valuable space in the footer area, making it less cluttered and more user-friendly.

  • Enhanced User Engagement: A well-organized footer can draw more attention to the newsletter subscription box, increasing the chances of visitors subscribing.

  • Improved Visual Appeal: A unified layout creates a clean and professional look, aligning with modern web design standards.

  • Increased Conversion Rates: By making it easier for customers to find and subscribe to your newsletter, you can potentially increase your mailing list and conversion rates.

Footer Newsletter subscription section in Dawn 15 by default.

Cutomized Footer Newsletter subscription section.

Steps to Implement

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

To display the newsletter subscription box component in the same column as the menu in the footer, follow these steps:

  1. Open Your Theme Code Editor: Access your Shopify theme's code editor.

  2. Edit the CSS File: Locate and open either the section-footer.css or base.css file.

  3. Insert the Custom CSS Code: Add the following code to position the newsletter subscription box in the same column as the menu:

    .footer__content-top {
      display: flex !important;
      align-items: baseline;
      justify-content: space-around;
    }
    
    .footer__blocks-wrapper {
      width: 70%; // Adjust based on the requirement
    }
    
    .footer-block__newsletter .footer-block__heading {
      text-align: left;
    }
    
    @media screen and (max-width: 749px) {
      .footer__content-top {
        flex-direction: column;
      }
    }
  1. Save Your Changes: Save the modifications to your CSS file.

  2. Preview the Changes: Check your website's footer to ensure that the newsletter subscription box is displayed correctly alongside the menu.

By following these steps, you can effectively display the newsletter subscription box in the same column as the menu in your Shopify store's footer. This not only enhances the visual appeal of your site but also improves user engagement and optimizes the footer layout.

ByAnne

Thu Dec 19 2024
the blog image
Enhance Your Shopify Homepage with Autoplay Videos

Implementing autoplay videos on your Shopify homepage can be a game-changer for engaging customers as soon as they land on your site. This feature grabs attention immediately, providing a dynamic and visually appealing introduction to your store.

Benefits of Autoplay Videos

Implementing autoplay videos offers several advantages:

  • Increased Engagement: Videos capture attention more effectively than static images or text, keeping visitors on your site longer.

  • Enhanced User Experience: Provides a seamless and interactive experience, making your site feel more lively and modern.

  • Better Storytelling: Allows you to showcase your products or brand story in a compelling way, helping to build a stronger connection with your audience.

  • Boosted Conversions: Engaging video content can lead to higher conversion rates as customers are more likely to make a purchase after watching a video.

Steps to Implement

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

After uploading the video to Shopify, there are two options to achieve video autoplay feature.

First Option: Add Custom Liquid Code

If you’re using the Dawn theme version 13.0.0 or newer, follow these steps to add a custom liquid section for autoplay and loop features:

  1. Customize Your Theme: Click on “Customize” for your theme.

  2. Add a Section: In the customization panel, click on “Add section” and choose “Custom liquid.”

  3. Insert Custom Liquid Code: Paste the provided custom liquid code into the editor.

    <style>
    video {
    width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
    }
    </style>
    <video muted autoplay playsinline loop>
        <source src="/media/cc0-videos/websensepro.mp4"
                      type="video/mp4">
        <source src="/media/cc0-videos/websensepro.mp4"
                      type="video/mp4">
    </video>
    

Second Option: Auto Play Self-Hosted Video with Loop in Shopify

Create a new section named autoplay-video.liquid and paste the code. This will create a new section where you can select a video from your Shopify library.

<div 
  class="page-width"
  {% if section.settings.fullwidth %}
    style="max-width: 100%!important;margin: 0;padding:0px"
  {% endif %}
>
  <div class="video-section">
    <h2>{{ section.settings.heading }}</h2>
  </div>
  {% if section.settings.video_url.type == 'youtube' %}
    <div
      class="video-container"
      {% if section.settings.fullwidth %}
        style="width: 100%;"
      {% endif %}
    >
      <iframe
        src="https://www.youtube.com/embed/{{ section.settings.video_url.id }}{% if section.settings.autoplay %}?autoplay=1&mute=1{% endif %}{% if section.settings.controller == false %}&controls=0{% endif %}&modestbranding=1"
        class="youtube"
        width="100%"
        height="600px"
      ></iframe>
    </div>
  {% elsif section.settings.video_url.type == 'vimeo' %}
    <div class="video-container">
      <video
        src="https://player.vimeo.com/video/{{ section.settings.video_url.id }}{% if section.settings.autoplay %}?autoplay=1&mute=1{% endif %}"
        class="vimeo"
        {% if section.settings.fullwidth %}
          style="width: 100%;"
        {% endif %}
      ></video>
    </div>
  {% elsif section.settings.self_hosted_video %}
    <div
      class="video-container"
      {% if section.settings.fullwidth %}
        style="width: 100%;"
      {% endif %}
    >
      {{
        section.settings.self_hosted_video
        | video_tag:
          controls: section.settings.controller,
          autoplay: section.settings.autoplay,
          loop: true,
          width: '100%'
      }}
    </div>
  {% endif %}
</div>

{% if section.settings.fullwidth %}
  <style>
    /* Your full-height styles go here */
    .video-container {
      position: relative;
      width: 100%;
      padding-bottom: 56.25%; /* Adjust this value as needed for the aspect ratio */
      height: 0;
      overflow: hidden;
    }
    .video-container iframe,
    .video-container video {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
  </style>
{% endif %}

<script>
  document.addEventListener('DOMContentLoaded', function () {
    var videos = document.querySelectorAll('.youtube, .vimeo');
    function playPauseVideos() {
      videos.forEach(function (video) {
        if (section.settings.autoplay) {
          video.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
        } else {
          video.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
        }
      });
    }
    // Initial play/pause based on the 'Autoplay video' setting
    playPauseVideos();
    // Listen for changes in the 'Autoplay video' setting and play/pause accordingly
    document.getElementById('section_autoplay').addEventListener('change', function () {
      section.settings.autoplay = this.checked;
      playPauseVideos();
    });
  });
</script>
{% schema %}
{
  "name": "Video WebSensePro",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Video section heading",
      "default": "Video Autoplay"
    },
    {
      "type": "checkbox",
      "id": "fullwidth",
      "label": "Make Full Width"
    },
    {
      "type": "checkbox",
      "id": "autoplay",
      "label": "Autoplay video",
      "default": false
    },
    {
      "type": "checkbox",
      "id": "controller",
      "label": "Enable Controls",
      "default": false
    },
    {
      "type": "video",
      "id": "self_hosted_video",
      "label":"Select Video"
    }
  ],
  "presets": [
    {
      "name": "Video WebSensePro"
    }
  ]
}
{% endschema %}

If you wish to play a video hosted on YouTube or Vimeo, add the code below next to row 126 of above schema, then you’ll be able to paste a YouTube/Vimeo video link in the new section.

,
     {
      "type": "video_url",
      "id": "video_url",
      "accept": ["youtube", "vimeo"],
      "default": "https://www.youtube.com/watch?v=_9VUPq3SxOc",
      "label": "URL",
      "info": "Video plays on the page."
    }

ByAnne

Fri Dec 13 2024
the blog image
Unlock Higher Sales: The Power of Shopify Store Credit

Shopify’s latest store credit feature, launched in July 2024, is revolutionizing the way merchants engage with customers. This innovative tool allows shoppers to maintain a store balance that can be applied to future purchases, eliminating the need for traditional refunds or gift cards while incentivizing repeat business.

In this blog, we’ll explore how Shopify Store Credit works, its benefits, and how you can leverage it to increase sales and customer loyalty.

What is Shopify Store Credit?

Store credit is a balance assigned to a customer’s account that can only be used within your store. Unlike gift cards or discount codes, store credit is directly tied to a specific customer and cannot be transferred. Shopify now enables merchants to issue and manage store credit manually from the admin panel, making it easier than ever to reward customers and boost retention.

How Shopify Store Credit Drives Sales

Integrating store credit into your Shopify store offers several key advantages that drive sales and improve customer retention. Since store credit is directly tied to individual accounts, customers are more likely to return to your store instead of shopping elsewhere, fostering repeat purchases. It also streamlines operations by eliminating the need for issuing gift cards or processing refunds manually, making customer incentives and compensation more efficient.

Additionally, store credit encourages higher spending, as shoppers often go beyond their available balance, leading to increased average order values. Unlike traditional gift cards that require entering codes at checkout, store credit is seamlessly accessible within customer accounts, enhancing the overall shopping experience. Whether used for refunds, loyalty rewards, or special promotions, this versatile tool provides businesses with flexible ways to engage and retain their customers.

Maximizing Store Credit for Growth

Currently, Shopify allows merchants to issue store credit manually through the admin panel. However, many businesses are looking for automated solutions to streamline this process, particularly for loyalty programs.

Here’s how an automated store credit system can work in your Shopify store:

  1. Earn Store Credit on Purchases – Customers accumulate store credit based on their spending. For example, a 10% credit on a $1,000 purchase would add $100 to their balance.

  2. Seamless Checkout Experience – When customers log in, they see their store credit available at checkout and can apply it with a single click.

  3. Effortless Credit Management – Merchants can track balances, view transaction history, and analyze store credit performance directly in the Shopify admin. Custom apps can also provide insights into which purchases contributed to the credit balance.

Start Using Shopify Store Credit Today

Shopify’s store credit feature is more than just a replacement for gift cards—it’s a powerful strategy for increasing customer retention and sales. By implementing store credit, you transform every transaction into an opportunity for future revenue, keeping customers engaged and coming back.

If you’re interested in automating or customizing store credit for your Shopify store, let’s connect. We can help tailor a solution that aligns with your business goals and maximizes your sales potential.

ByKyle

Thu Nov 28 2024
the blog image
Optimize Your Shopify Store: Prevent Overselling with Quantity Selector Limits

Current Issue

Many Shopify store owners face a common challenge: customers can select more items in the quantity selector than are available in stock. For example, if you have a product with 11 pieces in stock, customers may still choose a quantity exceeding this limit.

When they click "Add to Cart," an error message appears stating, "You can't add more PRODUCT NAME to the cart." Unfortunately, this message does not inform customers of the actual quantity available, leaving them unsure of how many pieces they can add to their cart. This is particularly problematic for customers needing to purchase in large volumes and indicates a clear need for improvement in the customer experience.

Before:

After modification:

Requirement

To enhance user experience and avoid overselling, we need to:

  1. Prevent customers from adding more items to their cart than the available stock.

  2. Display the exact quantity of each item on hand to the customer.

Solution

To set a limit on your quantity picker, follow these simple steps:

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Step 1: Open Your Theme Code Editor

  • Navigate to your Shopify admin.

  • Go to Online Store and then Themes.

  • Click on Actions and select Edit Code.

Step 2: Find the File main-product.liquid

  • In the left-hand sidebar, locate the Sections folder.

  • Open the file main-product.liquid.

Step 3: Locate the Quantity Input Code

  • Search within this file for the quantity_input code.

tep 4: Add Necessary Attributes

Copy and paste the following code into the quantity_input box to set the limit:

max="{{ product.selected_or_first_available_variant.inventory_quantity }}"
                        data-max="{{ product.selected_or_first_available_variant.inventory_quantity }}"

Step 5: Find and open the product-info.js file

In the asset folder, locate the updateQuantityRules function.

Step 6: Replace the code with the following:

const attributes = ['data-cart-quantity', 'data-min', 'data-max', 'step', 'max'];

This ensures that customers cannot select more items than what is available in stock and informs them of the exact quantity on hand.

By implementing these steps, you can effectively manage stock levels and enhance your customers' shopping experience. No more disappointed customers—just a smooth, efficient checkout process.

BySonya

Wed Nov 27 2024
the blog image
Syncing Shopify with Microsoft Dynamics GP: A Case Study

In today’s digital world, businesses increasingly turn to e-commerce platforms like Shopify to expand their reach and boost online sales. At the same time, many rely on powerful ERP systems like Microsoft Dynamics GP to manage core operations. However, the lack of integration between these systems often results in manual data entry for orders, customer details, and inventory updates—leading to inefficiencies, errors, and added operational overhead.

This case study highlights how we developed a custom integration solution to synchronize data between Shopify and Microsoft Dynamics GP, helping businesses streamline operations and achieve a unified view of their online and offline activities.

Project Goals: Enabling a Smooth Data Flow

Our objective was to develop an application that automates data exchange between Shopify and Microsoft Dynamics GP, eliminating manual processes and ensuring data consistency. Specifically, we aimed to:

  • Automate Data Synchronization – Enable real-time or scheduled data transfers, ensuring both systems remain up-to-date.

  • Implement Flexible Synchronization Strategies – Design adaptable data sync plans to optimize efficiency and accuracy across different data types.

Implementation: A Step-by-Step Approach

Requirement Analysis

We conducted an in-depth assessment of the client’s business processes, identifying critical data points for synchronization, required update frequency, and specific business rules. This was documented in a detailed requirements specification to align expectations.

Technology Selection

C# was chosen for its strong compatibility with Microsoft Dynamics GP. The integration leveraged:

  • Shopify REST API – To extract necessary data from Shopify.

  • Dynamics GP Web Service – To securely transmit data into Dynamics GP.

Development Process

Our solution centered around two key components:

  • Data Retrieval – Extracting relevant information from Shopify using its REST API.

  • Data Transmission – Pushing data securely into Dynamics GP via its Web Service.

Overcoming Challenges

During integration, we tackled several challenges:

  • Limited Documentation – Microsoft Dynamics GP’s lack of detailed documentation required extensive research and testing.

  • Logical Adjustments – Custom synchronization logic was necessary to align with the client’s business workflows and data structures.

  • Web Service Configuration Issues – Some parameters failed to sync due to GP Web Service limitations, requiring manual configuration adjustments to resolve.

The Solution: A Robust Synchronization Engine

Our integration solution featured:

  • Custom Synchronization Rules – Tailored data transformation and synchronization logic for accurate, consistent migration.

  • Error Handling Mechanism – A comprehensive error-logging and retry system to ensure data integrity.

Deliverables: Equipping the Client for Success

We provided:

  • Source Code Package – Complete source code and configuration files for future maintenance and scalability.

  • User Guide – A detailed guide covering installation, configuration, and troubleshooting.

Quality Assurance: Ensuring a Seamless Experience

To ensure a stable and high-performing solution, we maintained ongoing collaboration with the client to refine and validate requirements. Our team conducted thorough design reviews, involving both internal and external stakeholders, to guarantee a scalable and efficient architecture. Extensive unit testing was performed to verify the reliability of individual components, followed by integration testing in a dedicated environment mirroring the production setup to ensure seamless data flow. 

We also carried out performance testing, simulating high-load scenarios to assess system stability. Before deployment, we engaged the client in structured User Acceptance Testing (UAT), incorporating their feedback to fine-tune the application. Finally, we implemented continuous monitoring and established a maintenance schedule to track real-time performance and ensure long-term stability.

Conclusion: Driving Business Efficiency

By integrating Shopify with Microsoft Dynamics GP, we helped the client:

  • Eliminate Manual Data Entry – Automating synchronization freed up valuable time and resources.

  • Improve Data Accuracy – Reduced errors and ensured consistent data across platforms.

  • Gain Real-time Visibility – A unified view of online and offline operations enabled better decision-making.

ByKyle

Mon Nov 25 2024
the blog image
Automate Gift Card Issuance for Orders Over Certain Value in Shopify: A Shopify Flow Tutorial

In this guide, we will explore how to automatically send gift cards to customers who successfully place an order exceeding a specified amount. This process can enhance customer satisfaction and encourage repeat business.

Business Scenario

The client wants to implement an automated system that sends a gift card to customers after they complete an order that meets a predetermined price threshold.

Solution Overview

The solution involves creating a logical flow that checks the order details and issues a gift card based on specific conditions. Here’s how it works:

  1. Order Marking: When a gift card is sent, the system marks the associated order as paid.

  2. Order Amount Check: The system verifies if the total order amount exceeds a specified limit.

  3. Gift Card Issuance Decision: If the order total is above the set amount, the system prepares to issue a gift card.

  4. Additional Name Condition: To prevent unintended gift card issuance, an additional check ensures that the item names in the order do not match the gift card name.

  5. Flow Termination: If the order fails this name condition, the process halts, and no gift card is issued.

By implementing this dual-condition check (amount and name), the system ensures that gift cards are only issued when both criteria are satisfied.

Implementation Steps

This automation can be achieved using Shopify Flow. Below are the detailed steps:

Step 1: Create a Gift Card Product

Set up a gift card product with the desired value for distribution.

Step 2: Access Shopify Flow

Navigate to Shopify Flow and create a new workflow.

Step 3: Set Workflow Trigger

Start with the trigger labeled "Order Paid."

Step 4: Add Conditions

Insert a condition that checks if the order amount is greater than or equal to $199.

Ensure that none of the item names in the order match the name of the gift card (e.g., "Free Gift Card - £1,000.00").

Step 5: Select Gift Card Product

Add an action to select the gift card product as an "Order line item."

Make sure to uncheck the box for "Send email to notify customer." This prevents sending a bill for the gift card, which could confuse customers.

Step 6: Mark as Paid

Finally, add an action labeled "Mark as Paid." This will automatically mark the added gift card as paid, ensuring that customers receive a separate email with their gift card details.

Customer Notification

Once this workflow is activated, customers will receive an email containing their gift card information. Additionally, they can view the gift card added to their order in Shopify Admin.

Considerations

One limitation of this approach is that while the gift card is marked as paid manually, its value may still appear in the total order amount. This could lead to confusion regarding the actual total due from customers. By following these steps, you can effectively automate the issuance of gift cards based on customer orders, enhancing your customer engagement strategy while minimizing manual intervention.

Conclusion

In summary, automating gift card issuance for orders over $199 can significantly improve customer satisfaction and loyalty. By implementing this straightforward workflow in Shopify Flow, you can streamline your operations and enhance your marketing efforts.

ByKate

Mon Nov 04 2024
the blog image
How to Implement a Transparent Header in Your Shopify Theme

The Benefits of a Transparent Header

In today's digital landscape, a transparent header is a popular design trend that offers a visually striking effect when customers visit your site. Unlike traditional headers with a solid color, 

a transparent header enhances your brand image and offers several key benefits:

  • Aesthetic Appeal: Transparent headers provide a clean and modern look that can elevate your site's overall design.

  • Improved User Experience: They enable a smoother visual transition as users scroll, making navigation more intuitive.

  • Seamless Integration: A transparent header blends seamlessly with your page content, creating a cohesive visual experience.

  • Prominent Branding: With no distracting background, your brand elements become more prominent and impactful.

Visual Comparison

Here’s a comparison to illustrate the difference:

Traditional Header:

Transparent Header:

Step-by-Step Guide to Implementing a Transparent Header

To integrate a transparent header into your Shopify theme, follow these steps:

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

  1. Modify the Header.liquid File: Access the header.liquid file within the Sections folder of your theme. Insert the following code to achieve the transparent effect:

{% if template == 'index' %}
  <style>
    .section-header{margin-bottom:0}.header-wrapper--border-bottom{border-bottom:.1rem solid rgba(255,255,255,.08)}.overflow-hidden-desktop .header-wrapper,.overflow-hidden-tablet .header-wrapper{position:initial;width:initial;background:initial;color:initial;background:var(--gradient-background)}.header-wrapper summary.header__menu-item+ul span,.header-wrapper summary.header__menu-item+ul svg,.overflow-hidden-desktop .header-wrapper .disclosure__button svg,.overflow-hidden-desktop .header-wrapper .disclosure__button>span,.overflow-hidden-desktop .header-wrapper .list-menu span,.overflow-hidden-desktop .header-wrapper a svg,.overflow-hidden-desktop .header-wrapper details>span,.overflow-hidden-desktop .header-wrapper h1.header__heading span.h2,.overflow-hidden-desktop .header-wrapper summary svg,.overflow-hidden-desktop details[open]>.header__menu-item,.overflow-hidden-tablet .header-wrapper .disclosure__button svg,.overflow-hidden-tablet .header-wrapper .disclosure__button>span,.overflow-hidden-tablet .header-wrapper .list-menu span,.overflow-hidden-tablet .header-wrapper a svg,.overflow-hidden-tablet .header-wrapper details>span,.overflow-hidden-tablet .header-wrapper h1.header__heading span.h2,.overflow-hidden-tablet .header-wrapper summary svg,.overflow-hidden-tablet details[open]>.header__menu-item,.scrolled-past-header .header-wrapper .disclosure__button svg,.scrolled-past-header .header-wrapper .disclosure__button>span,.scrolled-past-header .header-wrapper .list-menu span,.scrolled-past-header .header-wrapper a svg,.scrolled-past-header .header-wrapper details>span,.scrolled-past-header .header-wrapper h1.header__heading span.h2,.scrolled-past-header .header-wrapper summary svg,.scrolled-past-header details[open]>.header__menu-item{color:rgba(var(--color-foreground),.75)!important}.header-wrapper{position:absolute;width:100%;background:0 0;color:#fff}.header-wrapper .disclosure__button svg,.header-wrapper .disclosure__button>span,.header-wrapper .list-menu span,.header-wrapper a svg,.header-wrapper details>span,.header-wrapper h1.header__heading span.h2,.header-wrapper summary svg,details[open]>.header__menu-item{color:#fff!important}.scrolled-past-header .header-wrapper{position:absolute;width:100%;background:0 0;color:rgb(var(--color-foreground))!important}.header-localization .disclosure .localization-form__select:hover{text-decoration-color:white}
    .scrolled-past-header .header-wrapper {
      background: #fff;
    }
  </style>
{% endif %}
  1. Save and Review: Save your changes and review the header on different devices to ensure it displays correctly across all platforms.

By incorporating a transparent header, you can enhance the visual appeal and user experience of your Shopify store. This small adjustment can make a significant impact on how customers 

interact with your site, ultimately benefiting your brand's image and sales.

ByAnne

Fri Oct 18 2024
the blog image
How to Add Breadcrumbs in Shopify's Dawn 15 Theme

In this tutorial, we will address a frequent question from the Shopify community: how to add breadcrumbs to the Dawn 15 theme. Breadcrumbs are an essential feature that helps users navigate your site more efficiently, improving customer experience and potentially increasing sales revenue.

Understanding Breadcrumbs

Breadcrumbs are navigation aids that show users the path they have taken to arrive at a particular page. They enable users to easily find the collection they are viewing while on a product detail page by clicking on the breadcrumb path.

To add breadcrumbs to the Shopify Dawn 15 theme, follow these steps:

Before making any code modifications, always ensure you create a copy of the theme first. Test all your changes on this copy to confirm everything works perfectly before publishing it to the live theme. This precaution helps prevent any issues from affecting your main theme and keeps your work safe.

Creating a Breadcrumb Section for Shopify's Dawn 15 Theme

Add a New File

Begin by adding a new file in the sections folder of your theme. Name this file breadcrumb.liquid. Open the file and replace the content with the code provided. Save your changes.

{%- style -%}
  {% if template == 'product' and section.settings.enable_product_page %}
  .breadcrumbs{
  display: block;
  }
  {% else %}
  .breadcrumbs{
  display: none;
  }
  {% endif %}

  {% if section.settings.enable_complete_site %}
  .breadcrumbs{
  display: block;
  }
  {% endif %}

  {% if template == 'collection' and section.settings.enable_collection_page %}
  .breadcrumbs{
  display: block;
  }
  {% endif %}


  :root {
  --svg_fill: {{ section.settings.breadcrumb_text_color}}
  ;
  }

  .home-icon-container {
  display: inline-block;
  margin-right: 10px;
  margin-left: 10px;
  margin-top: 10px;
  vertical-align: sub;
  }

  .breadcrumbs {
  padding: 1rem 5rem;
  color: {{ section.settings.breadcrumb_text_color }}
  ;
  background-color: {{ section.settings.breadcrumb_bg_color }};
  text-transform: lowercase !important;
  }

  .breadcrumbs li {
  display: inline-block;
  }

  .breadcrumbs a {
  text-decoration: none;
  /*Will put liquid customization here*/
  font-size: 15px;
  color: inherit;
  }


  {%- if section.settings.breadcrumb_accent_color_bool -%}
  .breadcrumbs a:last-of-type {
  color: {{ section.settings.breadcrumb_accent_color }}
  !important;
  }
  {%- endif -%}


  .breadcrumb-delimeter:not(:last-child):after {
  {%- case section.settings.breadcrumb_delimeter -%}
  {%- when "angle_right" -%}
  content: "›";
  font-size: 18px;

  {%- when "slash" -%}
  content: "/";
  font-size: 16px;

  {%- when "arrow_right" -%}
  content: "➤";
  font-size: 15px;

  {%- when "squiggle_arrow" -%}
  content: "\27FF";
  font-size: 25px;

  {%- when "right_long" -%}
  content: "\279E";
  font-size: 18px;

  {%- when "double_right" -%}
  content: "\00BB";
  font-size: 20px;
  {%- when "diamond_arrow_head" -%}
  content: "⤞";
  font-size: 25px;

  {%- when "heavy_angle_right" -%}
  content: "\276F";
  font-size: 18px;

  {%- else -%}

  {%- endcase -%}
  display: inline-block;
  margin-left: 0.75rem;
  margin-right: 0.50rem;
  speak: none;
  }

  .breadcrumbs [aria-current="page"] {
  color: inherit;
  font-weight: normal;
  text-decoration: none;
  }
{%- endstyle -%}

<div class="page-width breadcrumbs" aria-label="breadcrumbs">
  {%- unless template == 'index' or template == 'cart' -%}
    {%- if section.settings.breadcrumbs_home_icon_bool -%}
      <div class="home-icon-container">
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="20"
          height="20"
          fill="currentColor"
          class="bi bi-house-door-fill"
          viewBox="0 0 16 16"
        >
          <path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z" />
        </svg>
      </div>
    {%- endif -%}

    <a href="/" title="Home">Home</a>
    {%- case template.name -%}
      {%- when 'article' -%}
        {%- for link in linklists['main-menu'].links -%}
          {%- if link.url == blog.url -%}
            <span class="breadcrumb-delimeter" aria-hidden="true"></span>
            <!-- Breadcrumb Delimeter -->
            {{ link.title | link_to: link.url }}
            {% break %}
          {%- endif -%}
        {%- endfor -%}
        <span class="breadcrumb-delimeter" aria-hidden="true"></span>
        <a href="{{ article.url }}" aria-current="page">{{ article.title }}</a>
      {%- when 'product' -%}
        {%- capture product_url_string -%}
{%- for collection in product.collections -%}
{{ collection.url }}|
{%- endfor -%}
{%- endcapture -%}

        {%- assign object_url_string = product_url_string | append: product.url -%}

        {%- assign object_urls = object_url_string | split: '|' -%}
        {%- capture linklist_titles_str -%}
{%- for linklist in linklists -%}
{{ linklist.title | handleize }}|{%- endfor -%}
{%- endcapture -%}
        {%- assign str_size = linklist_titles_str | size | minus: 1 -%}
        {%- assign linklist_titles_str = linklist_titles_str | slice: 0, str_size -%}
        {%- assign linklist_titles = linklist_titles_str | split: '|' -%}

        {%- assign level = 1 -%}
        {%- for link in linklists['main-menu'].links -%}
          {%- assign link_handle = link.title | handle -%}
          {%- assign link_titles = link_titles | append: link.title | append: '|' -%}
          {%- assign link_urls = link_urls | append: link.url | append: '|' -%}                                                                                                                                    
          {%- assign link_levels = link_levels | append: level | append: '|' -%}
          {%- assign link_parents = link_parents | append: 'main-menu' | append: '|' -%}
          {%- assign link_handles = link_handles | append: link_handle | append: '|' -%}

          {%- if linklist_titles contains link_handle -%}
            {%- for clink in linklists[link_handle].links -%}
              {%- if forloop.first == true -%}
                {%- assign level = level | plus: 1 -%}
              {%- endif -%}
              {% assign clink_handle = clink.title | handle %}
              {%- assign link_titles = link_titles | append: clink.title | append: '|' -%}
              {%- assign link_urls = link_urls | append: clink.url | append: '|' -%}
              {%- assign link_levels = link_levels | append: level | append: '|' -%}
              {%- assign link_parents = link_parents | append: link_handle | append: '|' -%}
              {%- assign handle = link.title | handleize -%}
              {%- assign link_handles = link_handles | append: clink_handle | append: '|' -%}

              {%- if linklist_titles contains clink_handle -%}
                {%- for gclink in linklists[clink_handle].links -%}
                  {%- if forloop.first == true -%}
                    {%- assign level = level | plus: 1 -%}
                  {%- endif -%}

                  {% assign gclink_handle = gclink.title | handle %}
                  {%- assign link_titles = link_titles | append: gclink.title | append: '|' -%}
                  {%- assign link_urls = link_urls | append: gclink.url | append: '|' -%}
                  {%- assign link_levels = link_levels | append: level | append: '|' -%}
                  {%- assign link_parents = link_parents | append: gclink_handle | append: '|' -%}
                  {%- assign link_handles = link_handles | append: gclink_handle | append: '|' -%}

                  {%- if forloop.last == true -%}
                    {%- assign level = level | minus: 1 -%}
                  {%- endif -%}
                {%- endfor -%}
              {%- endif -%}
              {%- if forloop.last == true -%}
                {%- assign level = level | minus: 1 -%}
              {%- endif -%}
            {%- endfor -%}
          {%- endif -%}
        {%- endfor -%}

        {%- comment -%} CONVERT TO ARRAYS {%- endcomment -%}
        {%- assign str_size = link_levels | size | minus: 1 -%}
        {%- assign llevels = link_levels | slice: 0, str_size | split: '|' -%}

        {%- assign str_size = link_titles | size | minus: 1 -%}
        {%- assign ltitles = link_titles | slice: 0, str_size | split: '|' -%}

        {%- assign str_size = link_handles | size | minus: 1 -%}
        {%- assign lhandles = link_handles | slice: 0, str_size | split: '|' -%}

        {%- assign str_size = link_parents | size | minus: 1 -%}
        {%- assign lparents = link_parents | slice: 0, str_size | split: '|' -%}

        {%- assign str_size = link_urls | size | minus: 1 -%}
        {%- assign lurls = link_urls | slice: 0, str_size | split: '|' -%}

        {%- assign depth = '3' -%}
        {%- assign bc3_parent_list_handle = '' %}

        {%- for url in lurls -%}
          {%- if object_urls contains url and llevels[forloop.index0] == depth -%}
            {%- unless url == product.url or url == collection.url -%}
              {%- capture bc3 -%}
                 {{ ltitles[forloop.index0] | link_to: url, ltitles[forloop.index0] }}
              {%- endcapture -%}
            {%- endunless -%}
            {%- assign bc3_parent_list_handle = lparents[forloop.index0] -%}
            {%- assign bc3_list_handle = lhandles[forloop.index0] -%}
            {% break %}
          {%- endif -%}
        {%- endfor -%}

        {%- assign depth = '2' -%}
        {%- assign bc2_parent_list_handle = '' %}

        {%- if bc3_parent_list_handle == '' -%}
          {%- for url in lurls -%}
            {%- if llevels[forloop.index0] == depth -%}
              {%- if object_urls contains url -%}
                {%- unless url == product.url or url == collection.url -%}
                  {%- capture bc2 -%}
{{ ltitles[forloop.index0] | link_to: url, ltitles[forloop.index0] }}{%- endcapture -%}
                {% endunless %}
                {%- assign bc2_parent_list_handle = lparents[forloop.index0] -%}
                {%- break -%}
              {%- endif -%}
            {%- endif -%}
          {%- endfor -%}
        {%- else -%}
          {%- for list_handle in lhandles -%}
            {%- if list_handle == bc3_parent_list_handle -%}
              {% assign bc2_list_handle = list_handle %}
              {%- assign bc2_parent_list_handle = lparents[forloop.index0] -%}
              {%- assign bc2_list_title = ltitles[forloop.index0] -%}
              {%- for bc2_sibling_link in linklists[bc2_parent_list_handle].links -%}
                {%- assign bc2_sibling_title_handleized = bc2_sibling_link.title | handle -%}
                {% if bc2_sibling_title_handleized == bc2_list_handle %}
                  {%- capture bc2 -%}
{{ bc2_sibling_link.title | link_to: bc2_sibling_link.url, bc2_sibling_link.title }}{%- endcapture -%}
                  {% break %}
                {%- endif -%}
              {%- endfor -%}
              {% break %}
            {%- endif -%}
          {%- endfor -%}
        {%- endif -%}

        {%- assign depth = depth | minus: 1 | append: '' -%}
        {%- assign bc1_parent_list_handle = '' %}

        {%- if bc2_parent_list_handle == '' -%}
          {% for url in lurls %}
            {%- if object_urls contains url and llevels[forloop.index0] == depth -%}
              {%- unless url == product.url or url == collection.url -%}
                {%- capture bc1 -%}
{{ ltitles[forloop.index0] | link_to: url, ltitles[forloop.index0] }}{%- endcapture -%}
              {% endunless %}
              {%- assign bc1_parent_list_handle = lparents[forloop.index0] -%}
              {%- break -%}
            {%- endif -%}
          {%- endfor -%}

        {%- else -%}
          {%- for list_handle in lhandles -%}
            {%- if bc2_parent_list_handle == list_handle -%}
              {% assign bc1_list_handle = list_handle %}
              {%- assign bc1_parent_list_handle = lparents[forloop.index0] -%}
              {%- assign bc1_title = ltitles[forloop.index0] -%}
              {%- for bc1_sibling_link in linklists[bc1_parent_list_handle].links -%}
                {%- assign bc1_sibling_title_handleized = bc1_sibling_link.title | handle -%}
                {% if bc1_sibling_title_handleized == bc1_list_handle %}
                  {%- capture bc1 -%}
{{ bc1_sibling_link.title | link_to: bc1_sibling_link.url, bc1_sibling_link.title }}{%- endcapture -%}
                  {% break %}
                {%- endif -%}
              {%- endfor -%}
            {%- endif -%}
          {%- endfor -%}
        {%- endif -%}

        {%- if bc1 -%}
          <span class="breadcrumb-delimeter" aria-hidden="true"></span>
          <!-- Breadcrumb Delimeter -->
          {{ bc1 }}
        {%- endif -%}
        {%- if bc2 -%}
          <span class="breadcrumb-delimeter" aria-hidden="true"></span>
          {{ bc2 }}
        {%- endif -%}
        {%- if bc3 -%}
          <span class="breadcrumb-delimeter" aria-hidden="true"></span>
          {{ bc3 }}
        {%- endif -%}

        <span class="breadcrumb-delimeter" aria-hidden="true"></span>
        <a href="{{ product.url }}">{{ product.title }}</a>
      {%- else -%}
        {% for link in linklists['main-menu'].links %}
          {% if link.child_active or link.active %}
            <span class="breadcrumb-delimeter" aria-hidden="true"></span>
            <!-- Breadcrumb delimeter -->
            <a href="{{ link.url }}">
              {{ link.title | escape }}
            </a>
            {% for clink in link.links %}
              {% if clink.child_active or clink.active %}
                <span class="breadcrumb-delimeter" aria-hidden="true"></span>
                <a href="{{ clink.url }}">
                  {{ clink.title | escape }}
                </a>
                {% for gclink in clink.links %}
                  {% if gclink.child_active or gclink.active %}
                    <span class="breadcrumb-delimeter" aria-hidden="true"></span>
                    <a href="{{ gclink.url }}">
                      {{ gclink.title | escape }}
                    </a>
                  {% endif %}
                {%- endfor -%}
              {% endif %}
            {%- endfor -%}
          {% endif %}
        {%- endfor -%}
    {%- endcase -%}
  {%- endunless -%}
</div>

<script defer>
  const breadCrumbLinks = document.querySelectorAll('.breadcrumbs a');
  const lastLink = breadCrumbLinks[breadCrumbLinks.length - 1];
  lastLink.href = 'javascript&colon;void(0)';
</script>

{% schema %}
{
  "name": "Breadcrumb Navigation",
  "settings": [
    {
      "type": "header",
      "content": "Subscribe to our [Shinetech Changchun](https://www.youtube.com/@ShinetechShopidev)"
    },
    {
      "type": "checkbox",
      "id": "breadcrumbs_home_icon_bool",
      "label": "Use the home icon next to the Home link in the breadcrumb",
      "default": true
    },
    {
      "type": "select",
      "id": "breadcrumb_delimeter",
      "label": "Breadcrumb Delimeter Icon",
      "options": [
        {
          "value": "angle_right",
          "label": "Angle Right"
        },
        {
          "value": "slash",
          "label": "Slash"
        },
        {
          "value": "arrow_right",
          "label": "Arrow Right"
        },
        {
          "value": "squiggle_arrow",
          "label": "Squiggle Arrow"
        },
        {
          "value": "right_long",
          "label": "Right Long"
        },
        {
          "value": "double_right",
          "label": "Double Right"
        },
        {
          "value": "diamond_arrow_head",
          "label": "Diamond Arrow Head"
        },
        {
          "value": "heavy_angle_right",
          "label": "Heavy Angle Right"
        }
      ],
      "default": "angle_right"
    },
    {
      "type": "color",
      "id": "breadcrumb_bg_color",
      "label": "Background Color",
      "default": "#fff"
    },
    {
      "type": "color",
      "id": "breadcrumb_text_color",
      "label": "Breadcrumb Color",
      "default": "#465076"
    },
    {
      "type": "checkbox",
      "id": "breadcrumb_accent_color_bool",
      "label": "Enable visited page color link in breadcrumb",
      "default": false
    },
    {
      "type": "color",
      "id": "breadcrumb_accent_color",
      "label": "Visited Page Link Color",
      "default": "#4770db"
    },
    {
      "type": "checkbox",
      "id": "enable_product_page",
      "label": "Enable in Product Page"
    },
    {
      "type": "checkbox",
      "id": "enable_collection_page",
      "label": "Enable in Collection Page"
    },
    {
      "type": "checkbox",
      "id": "enable_complete_site",
      "label": "Enable on Complete Website",
      "default": true
    }
  ],
  "presets": [
    {
      "name": "Breadcrumb Navigation"
    }
  ]
}
{% endschema %}


Adding and Customizing the Breadcrumb Section in Dawn 15 Theme

  1. Add Breadcrumb Section in the Theme Editor: Access the theme editor and add the newly created section to the page templates where you want the breadcrumbs to appear, such as the collection listing page, product listing page, and product detail page. Repeat the same process for any other pages where you wish to add breadcrumbs.

  2. Customize the Appearance: Adjust the color to match your brand. Position the newly added breadcrumbs section where desired.

By following these steps, your site will now feature functional breadcrumb navigation. 

Important:

This solution is tailored to the Dawn 15 theme. For other themes, custom adjustments may be necessary.

BySonya

Fri Oct 18 2024