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
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.
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.
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
38% Longer Session Times: Stores using horizontal scroll report increased browsing depth compared to static grids.
Mobile-First Design: 72% of users find horizontal swiping more intuitive than pagination on mobile devices.
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
In Shopify Admin:
Navigate to Online Store > Themes > Customize.
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.
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
Potential Conversion Lift: Stores using scrolling marquees have reported increases in conversion rates by highlighting time-sensitive offers, though the exact impact can vary.
Mobile Optimization: With 65% of Shopify traffic from mobile devices, compact scrolling text efficiently utilizes limited screen space.
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
In Shopify Admin, navigate to Online Store > Themes > Customize.
Add your new "Custom Marquee" section to the header or product template.
Strategic Optimization Tips
Speed & Readability:
Optimal duration: 15-25 seconds per cycle.
Contrast ratio ≥ 4.5:1 for accessibility.
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.
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.
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>
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!
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:
Navigate to the Online Store: In your Shopify Admin, go to Online Store.
Customize Your Theme: Under Themes, click Customize next to the Dawn theme (or your active theme).
Open Theme Settings: In the theme editor, locate Theme settings at the bottom left.
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:
Edit the Code: In your Shopify Admin, go to Online Store > Themes > Actions > Edit Code.
Locate the File: Open the
config/settings_schema.json
file.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:
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).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:
Access the Theme Settings: Go to Online Store > Themes.
Customize the Theme: Click Customize next to your theme.
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.
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.
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.
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.
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.
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.
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:
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.
Modify
sections/main-product.liquid
: Open thesections/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 }
Update
snippets/product-media-gallery.liquid
: Locate and open thesnippets/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>
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>
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 -%}
Update
assets/product-info.js
: In this file, add a new function calledsetVariantImages
. 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"); } }) } }
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.
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:
Open Your Theme Code Editor: Access your Shopify theme's code editor.
Edit the CSS File: Locate and open either the section-footer.css or base.css file.
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; } }
Save Your Changes: Save the modifications to your CSS file.
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.
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:
Customize Your Theme: Click on “Customize” for your theme.
Add a Section: In the customization panel, click on “Add section” and choose “Custom liquid.”
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."
}
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:
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.
Seamless Checkout Experience – When customers log in, they see their store credit available at checkout and can apply it with a single click.
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.
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:
Prevent customers from adding more items to their cart than the available stock.
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.
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.
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:
Order Marking: When a gift card is sent, the system marks the associated order as paid.
Order Amount Check: The system verifies if the total order amount exceeds a specified limit.
Gift Card Issuance Decision: If the order total is above the set amount, the system prepares to issue a gift card.
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.
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.
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.
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 %}
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.
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: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
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.
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.