Add automatic slider / carousel

Product / Card Automatic Carousel / Slider for Most of Shopify Theme

How to see the code

1. Please log in/create an account first. This is to ensure that all purchases are connected to an account and can be viewed on different devices.

2. Purchase the code using the "purchase box" on the specific page of the desired code.

3. After the purchase, please refresh the page.

I was able to write an autoplay slider / carousel using scroll. This works with most of Shopify themes (work best in Dawn and Debut theme). 

In this tutorial, we are creating a slider or carousel for a card or product card. I did provide an option to choose between the two. I also included an autoplay option, of course. You can choose from 3 to 7 products per slide, or 3 - 5 cards per slide.

I also added an options for dots and number counter. Yes, the counters work.

 Check DEMO store here💻. Password: made4uo

 

 To start:

1. Go to Admin Shopify store > Themes > Actions > Edit Code
2. In Section folder, create a new section, and name it "universal-carousel", then paste the code below.

 {%- liquid assign products_to_display = section.settings.collection.all_products_count
if section.settings.collection.all_products_count > section.settings.products_count
assign products_to_display = section.settings.products_count
endif
-%}
{% assign slideNum = section.settings.slide_number %}

<div class="carouselContainer" id="section--{{section.id}}">
<div class="topWrapper">
{% if section.settings.title != blank %} <h2>{{section.settings.title}}</h2>{% endif %}
{% if section.settings.view_all == true and section.settings.type == 'product'%}
<a href="{{ section.settings.collection.url }}" id="viewAllBtn">
<span>View All</span>
</a>
{% endif %}
</div>
<carousel-component
data-type="{{section.settings.type}}"
data-autoplay="{{section.settings.autoplay}}"
data-interval="{{section.settings.interval | times: 1000 }}"
data-infinite-scroll="{{section.settings.infinite}}"
data-counter-type="{{section.settings.counter_type}}">
<div class="carouselWrapper">
<ul class="carouselBox">
{% case section.settings.type %}
{% when "product" %}
{%- for product in section.settings.collection.products limit: section.settings.products_count-%}
<li class="carouselSlider" id="slide-{% increment %}">
{% assign product_image = product.selected_or_first_available_variant %}
<a class="productBox" href="{{ product.url | within: collection }}" style="text-decoration: none;">
<div class="carouselImage">
<img src="{{ product.featured_image | img_url: 'master'}}"
alt="{{ product.images.alt | escape }}"/>
</div>
<div class="detailsText">
<span class = "productTitle"> {{ product.title }}</span>
<span class = "productPrice"> {{ product.price | money }}</span>
</div>
</a>
</li>
{% endfor %}


{% when "card" %}
{% if section.settings.slide_number > 5 %}
{% assign section.settings.slide_number = 4 %}
{% else %}
{% assign slide_number = section.settings.slide_number %}
{% endif %}
{% for block in section.blocks %}
<li class="carouselSlider"
id="slide-{% increment %}">
<a class="cardWrapper" {% if block.settings.card_link != blank %}href="{{block.settings.card_link}}"{% endif %}>
{% assign img_url = block.settings.card_image %}
{% if img_url != blank %}
<div class="cardImageWrapper">
<div class="cardImage">
<img src="{{img_url | img_url: master}}"
srcset="{% case img_url.width %}
{% when >= 375 %}{{ img_url | img_url: '375x' }}375w,
{% when >= 750 %}{{ img_url | img_url: '750x' }}750w,
{% when >= 1100 %}{{ img_url | img_url: '1100x' }}1100w,
{% when >= 1500 %}{{ img_url | img_url: '1500x' }}1500w,
{% when >= 1780 %}{{ img_url | img_url: '1780x' }}1780w,
{% when >= 2000 %}{{ img_url | img_url: '2000x' }}2000w,
{% when >= 3000 %}{{ img_url | img_url: '3000x' }} 3000w,
{% when >= 3840 %}{{ img_url | img_url: '3840x' }}3840w,
{% else %} {{ img_url | img_url: 'master' }}w,
{% endcase %}"
loading="lazy"
alt="{{ img_url.alt | escape }}">
</div>
</div>
{% endif %}
{% if block.settings.card_description != blank or block.settings.title != blank %}
<div class="cardDescription">
{% if block.settings.title != blank %}<h2 class="cardTitle">{{block.settings.title}}</h2>{% endif %}
{{block.settings.card_description}}
</div>
{% endif %}
</a>
</li>
{% endfor %}
{% endcase %}
</ul>
</div>
<div class="controlContainer">
<div class="btnContainer">
<button type="button" class="controlWrapper" id="prevSlide">
<span class="controlBtn prevBtn" ></span>
</button>
<ul class="slideDotContainer {% if section.settings.counter_type == 'dots' %}numberDot{% endif %}">
{% case section.settings.counter_type %}
{% when 'dots' %}
{% if section.settings.type == 'card' %}
{% for block in section.blocks %}
<li class="slideDots {% if forloop.first %}activeDot{% endif %}" data-dot-id="{{forloop.index0}}"></li>
{% endfor %}
<span class="activeNumber">1</span><span> / </span> <span>{% if section.settings.type == 'product' %} {{products_to_display}}{% endif %}
{% if section.settings.type == 'card' %}{{section.blocks.size}}{% endif %}</span>
{% endif %}

{% if section.settings.type == 'product' %}
{% for product in section.settings.collection.products limit: section.settings.products_count %}
<li class="slideDots {% if forloop.first %}activeDot{% endif %}" data-dot-id="{{forloop.index0}}"></li>
{% endfor %}

<span class="activeNumber">1</span> <span>/</span> <span>{% if section.settings.type == 'product' %} {{products_to_display}}{% endif %}
{% if section.settings.type == 'card' %}{{section.blocks.size}}{% endif %}</span>
{% endif %}

{% when 'number' %}
<span class="activeNumber">1</span> / <span>{% if section.settings.type == 'product' %} {{products_to_display}}{% endif %}
{% if section.settings.type == 'card' %}{{section.blocks.size}}{% endif %}</span>
{% endcase %}

</ul>
<button type="button" class="controlWrapper" id="nextSlide" >
<span class="controlBtn nextBtn"></span>
</button>
</div>
{% if section.settings.autoplay == true %}
<button type="button" class="pauseContainer" id="autoplaySlide">
<span class="pauseBtn"></span>
<span class="playBtn"> </span>
</button>
{% endif %}
</div>
</carousel-component>
</div>

<style>
#section--{{section.id}} {
--text-color: {{section.settings.text_color}};
--card-bg-color: {{section.settings.card_bg_color}};
--button-color:{{section.settings.button_color}} ;
--button-bg-color: {{section.settings.button_bg_color}};
--box-size: calc(100% / {{slideNum}});
}

carousel-component {
width: 100%;
}

.carouselContainer {
position: relative;
display: flex;
flex-direction: column;
justify-items: center;
align-items: center;
margin: 50px;
}

.topWrapper {
display: flex;
justify-content: space-between;
width: 100%;
align-items: center;
margin-bottom: 1rem;
}

.topWrapper h2 {
margin: auto;
}

.carouselWrapper {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
}

.carouselBox{
width: 100%;
padding: 3.5% 0;
display: flex;
align-items: flex-start;
margin: 0;
flex-wrap: inherit;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: none;
scrollbar-width: none;
}

.carouselBox::-webkit-scrollbar {
display: none;
}

.carouselSlider {
min-width: var(--box-size);
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding-right: 2%;
transition: all ease .5s;
}
/*===== PRODUCT ==== */
.productBox {
background: var(--bg-color);
display: flex;
text-decoration: none;
height: 100%;
flex-direction: column;
justify-content: space-between;
}

.productBox:hover {
background: var(--box-bg-color);
transform: scale(1.05);
}

.carouselImage {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center center;
}

.carouselImage > img {
width: 100%;
}

.detailsText {
display: flex;
flex-direction: column;
align-items: flex-start;
line-height: 1.2;
font-size: clamp(10px, 1.5vw ,18px);
color: var(--text-color);
height: 100%;
width: 100%;
white-space: normal;
overflow: hidden;
padding: 5%;
}

.productPrice {
padding-top: 1%;
font-size: clamp(10px, 2vw ,18px);
font-weight: 600;
margin-top: 10px;
}

.numberDot > span {
display: none;
}

/* === NUMBER OF SLIDES === */
@media screen and (max-width: 750px) {
.carouselSlider {
height: 100%;
min-width: 33.5% !important;
}

.slideDots {
display: none !important;
}
.numberDot > span {
display: block !important;
}

}
@media screen and (max-width: 481px) {
.carouselContainer {
margin: 4%;
}
.carouselBox {
padding: 1.5% 0;
}
.carouselSlider {
padding-left: 4%;
min-width: 50% !important;
}
.detailsText {
font-size: 2.5vw;
}
.controlContainer:hover {
transform: scale(1.05);
}
#viewAllBtn {
line-height: 1.2;
}
}

/*===== CARD ==== */
.cardWrapper {
width: 100%;
height: 100%;
position: relative;
padding: 5%;
background-color: var(--card-bg-color);
overflow: hidden;
}
.cardWrapper:hover .cardImage {
transform: scale(1.05);
}
.cardWrapper:hover .cardImageWrapper {
overflow: visible;
transform: scale(1.05);
}

.cardImageWrapper {
position: relative;
width: 100%;
overflow: hidden;
grid-row: 1 / 2;
transition: all ease .5s;
}
.cardImage {
width: 100%;
height: 100%;
transition: all ease .5s;
overflow: hidden;
}

.cardImage > img {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
object-position: center center;
vertical-align: middle;
}

.cardTitle {
width: 100%;
margin: 5% 0;
text-align: center;
font-weight: 600;
font-size: clamp(9px, 1.5vw, 19px);
line-height: 1.2;
grid-row: 2 / 3;
color: var(--text-color);
}

.cardDescription {
width: 100%;
line-height: 1.2;
grid-row: 3 / 4;
padding: 5px;
}

.cardDescription p {
font-size: clamp(8px, 1.2vw, 18px);
margin: 0;
text-overflow: ellipsis;
color: var(--text-color);
text-align: center;
}

/* ====== CONTROLS ===== */
.controlContainer {
display: flex;
justify-content: center;
transition: all ease .5s;
}

.controlContainer button {
background-color: transparent;
}

.controlContainer button > span {
background-color: var( --button-color);
}

.controlContainer:hover {
transform: scale(1.05);
}
.btnContainer {
height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
color: var(--button-color);
padding: .5%;
}
.controlWrapper {
width: 50px;
height: inherit;
position: relative;
display: flex;
align-items: center;
cursor: pointer;
justify-content: space-evenly;
border: none;
transition: transform ease .5s;
}
.controlBtn {
display: inline-block;
width: 25px;
height: 15px;
background-color: var(--button-color);
border: none;
transition: transform ease .5s;
}

.controlWrapper:hover .controlBtn{
transform: scale(1.05);
}

.controlWrapper:hover {
transform: scale(1.05);
}

#nextSlide:disabled .controlBtn {
background-color: #c3c5b8;
}
.nextBtn {
-webkit-clip-path: polygon(15% 0, 65% 50%, 15% 100%, 0 85%, 35% 50%, 0 15%);
clip-path: polygon(15% 0, 65% 50%, 15% 100%, 0 85%, 35% 50%, 0 15%);
margin-right: -10px;
}
#prevSlide:disabled .controlBtn {
background-color: #c3c5b8;
}
.prevBtn {
-webkit-clip-path: polygon(100% 15%, 65% 50%, 100% 85%, 85% 100%, 35% 50%, 85% 0);
clip-path: polygon(100% 15%, 65% 50%, 100% 85%, 85% 100%, 35% 50%, 85% 0);
margin-left: -10px;
}
.pauseContainer {
position: relative;
width: 30px;
height: 30px;
margin-left: 1rem;
display: flex;
justify-content: center;
align-items: center;
border: none;
}
.pauseBtn {
width: 50%;
height: 50%;
position: absolute;
background-color: var(--button-color);
-webkit-clip-path: polygon(0% 0%, 0% 100%, 40% 100%, 40% 0, 60% 0, 60% 99%, 100% 100%, 100% 0%);
clip-path: polygon(0% 0%, 0% 100%, 40% 100%, 40% 0, 60% 0, 60% 99%, 100% 100%, 100% 0%);
}
.playBtn {
width: 50%;
height: 60%;
display: none;
position: absolute;
background-color: var(--button-color);
-webkit-clip-path: polygon(0 0, 0 100%, 100% 49%);
clip-path: polygon(0 0, 0 100%, 100% 49%);
}
.playBtn:hover {
transform: scale(1.05);
background-color: var( --button-color);
}
.pauseBtn:hover {
transform: scale(1.05);
background-color: var( --button-color);
}

/*=== DOT CONTROL ====*/
.slideDotContainer {
position: relative;
width: 100%;
height: inherit;
margin: 0 .5rem;
display: flex;
padding: 0;
justify-content: space-around;
align-items: center;
flex-wrap: inherit;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none;
}
.slideDots:hover {
transform: scale(1.05);
}
.slideDots {
position: relative;
background-color: transparent;
border: 2px solid var(--button-color);
width: 10px;
height: 10px;
border-radius: 50%;
margin: 0 1px;
list-style-type: none;
}
.activeDot {
background-color: var(--button-bg-color);
}

/*=== VIEW ALL CONTROL ====*/
#viewAllBtn {
display: inline-block;
border-radius: 5px;
padding: 5px 10px;
color: white;
font-weight: 600;
box-shadow: 2px 2px 2px black;
background-color: var(--button-color);
text-decoration: none;
text-align: center;
}
#viewAllBtn:hover::after {
content: "Products";
}

#viewAllBtn:active{
box-shadow: none;
transform: translateY(-2%);
}

</style>
{% schema %}
{
"name": "Universal Carousel",
"settings": [
{
"type": "text",
"id": "title",
"label": "carousel name"
},
{
"type": "select",
"id": "type",
"default": "product",
"label": "type of item",
"options": [
{
"value": "product",
"label": "product"
},
{
"value": "card",
"label": "card"
}
]
},
{
"type": "collection",
"id": "collection",
"label": "Collection"
},
{
"type": "checkbox",
"id": "autoplay",
"default": false,
"label": "Autoplay"
},
{
"type": "checkbox",
"id": "view_all",
"default": false,
"label": "Show view all button (only available in product type)"
},
{
"type": "checkbox",
"id": "infinite",
"default": false,
"label": "Infinite Scroll"
},
{
"type": "range",
"id": "interval",
"label": "interval",
"max": 15,
"min": 1,
"step": 1,
"unit": "s",
"default": 3
},
{
"type": "range",
"id": "slide_number",
"min": 3,
"max": 7,
"label": "Number of slides to display (desktop only)",
"default": 4
},
{
"type": "range",
"id": "products_count",
"min": 4,
"max": 100,
"label": "Number of products to display",
"default": 4
},
{
"type": "select",
"id": "counter_type",
"default": "number",
"label": "type of counter",
"options": [
{
"value": "dots",
"label": "dots"
},
{
"value": "number",
"label": "number"
}
]
},
{
"type": "header",
"content": "Section color"
},
{
"type": "color",
"id": "text_color",
"label": "Text color",
"default": "#232b2b"
},
{
"type": "color",
"id": "card_bg_color",
"label": "Card background color",
"default": "#232b2b"
},
{
"type": "color",
"id": "button_color",
"label": "Button color",
"default": "#232b2b"
},
{
"type": "color",
"id": "button_bg_color",
"label": "Button background color",
"default": "#232b2b"
}
],
"blocks": [
{
"type": "card",
"name": "card",
"settings": [
{
"type": "image_picker",
"id": "card_image",
"label": "card image"
},
{
"type": "text",
"id": "title",
"label": "title"
},
{
"type": "richtext",
"id": "card_description",
"label": "description",
"default": "<p>Write your drescription here</p>"
},
{
"type": "url",
"id": "card_link",
"label": "card link"
}
]
}
],
"presets": [
{
"name": "Universal Carousel"
}
]
}
{% endschema %}

 3. We need the file that contains the javascript. Under the asset folder, open the "theme.js" or "global.js" file and paste the code below.

 class CarouselComponent extends HTMLElement {
constructor() {
super();

this.slideBox = this.querySelector('.carouselBox');
this.slides = this.querySelectorAll('.carouselSlider');
this.slideLot = this.querySelector('.activeNumber');

this.prevBtn = this.querySelector("#prevSlide");
this.nextBtn = this.querySelector("#nextSlide");
this.pausePlay = this.querySelector(".pauseContainer");
this.slideDots = this.querySelectorAll('.slideDots');

this.counter = this.dataset.counterType;
this.interval = this.dataset.interval;
this.infinite = this.dataset.infiniteScroll;
this.autoplay = this.dataset.autoplay;

if(!this.slideDots) return;
this.slideDots.forEach( dot => dot.addEventListener('click', this.showDot.bind(this)));
this.timeout;
this.curSlide = 0;

if(this.autoplay == 'true') {
this.autoPlayFx();
this.infinite = 'true';
}else {
clearInterval(this.autoplaying);
this.infinite = 'false';
}

this.init()
const resizeObserver = new ResizeObserver(entries => this.init());
resizeObserver.observe(this.slideBox);

this.prevBtn.addEventListener("click", this.prevFx.bind(this));
this.nextBtn.addEventListener("click", this.nextFx.bind(this));
if(!this.pausePlay) return;
this.pausePlay.addEventListener("click", this.pauseFx.bind(this));
}

init() {
if (this.counter == 'number') {
this.slideLot.innerHTML = 1;
}
this.goToSlide(0);
}

autoPlayFx(){
clearInterval(this.autoplaying);
this.autoplaying = setInterval(this.nextFx.bind(this), this.interval);
}

pauseFx() {
this.pauseBtn = this.pausePlay.querySelector('.pauseBtn');
this.playBtn = this.pausePlay.querySelector('.playBtn');
if (this.autoplay === 'true'){
clearInterval(this.autoplaying);
this.autoplay = 'false';

this.playBtn.style.display = "block";
this.pauseBtn.style.display = "none";
}else{
if(this.curSlide == this.slides.length -1) {
this.curSlide = -1;
}
this.autoPlayFx();
this.autoplay = 'true';
this.playBtn.style.display = "none";
this.pauseBtn.style.display = "block";
}
}

prevFx () {
this.curSlide--;
if(this.curSlide < 0){
if(this.infinite == 'true') {
this.curSlide = this.slides.length - 1;
} else {
this.curSlide = 0;
}
}
this.goToSlide(this.curSlide );
this.activeSlide(this.curSlide );
}

nextFx () {
this.curSlide ++
if(this.curSlide == this.slides.length) {
if(this.infinite == 'true') {
this.curSlide = 0;
}
if(this.autoplay == 'true') {
this.curSlide = 0;
}
}
this.goToSlide(this.curSlide );
this.activeSlide(this.curSlide );
}

showDot(e) {
if(!e.currentTarget) return;
const currentDot = e.currentTarget.dataset.dotId;

this.activeSlide(currentDot);
}

goToSlide (slide) {
let leftScroll = slide * this.slides[0].clientWidth;
this.slideBox.scrollTo({left: leftScroll});
}

activeSlide(currentDot) {
this.slideDots.forEach(dot => dot.classList.remove('activeDot'));

if(this.infinite == 'false') {
if(currentDot == this.slides.length - 1) {
this.nextBtn.setAttribute('disabled', true);
} else {
this.nextBtn.removeAttribute('disabled');
}
if(currentDot == 0) {
this.prevBtn.setAttribute('disabled', true);
} else {
this.prevBtn.removeAttribute('disabled');
}
}

this.slideLot.innerHTML = currentDot + 1;
if(! this.slideDots[currentDot]) return;
this.slideDots[currentDot].classList.add('activeDot');
this.showDot(currentDot);

this.curSlide = currentDot;
this.goToSlide(currentDot );
}
}

customElements.define('carousel-component', CarouselComponent);

 

That is it. Let me know if you have questions (",)

Updated 3/18/22: You can add multiple of this section in one page. 

Copied!
Back to blog

1 comment

Thanks for great video. How can I set minimum card number to 1 ?

Tom

Leave a comment