Add automatic slider / carousel

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

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 lessen the number of slides in cards since the text will not be visible with more than 5.

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

NOTE: You do not need to download a link nor incorporate a min.js. I was able to create a slick carousel before, see this link but it will not allow me to have multiple slider since both use the same min.js. 

 

 

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.

 {{ 'universal-carousel.css' | asset_url | stylesheet_tag }}

{%- 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
-%}

<div data-carousel-id="carousel-{{section.id}}" class="carouselContainer">
<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>
<div class="carouselWrapper
{% case section.settings.slide_number %}
{% when 2 %} two
{% when 3 %} three
{% when 4 %} four
{% when 5 %} five
{% when 6 %} six
{% when 7 %} seven
{% endcase %}">
<ul class="carouselBox" id="carouselBox"
data-slide="{{section.settings.slide_number}}"
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}}">
{% 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;">
<img class="carouselImage" src="{{ product.featured_image | img_url: 'master'}}"
alt="{{ product.images.alt | escape }}"/>
<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
{% case section.settings.slide_number %}
{% when 3 %} three
{% when 4 %} four
{% when 5 %} five
{% when 6 %} four
{% when 7 %} four
{% endcase %}"
id="slide-{% increment %}">
<div class="cardWrapper">
{% assign img_url = block.settings.card_image %}
<div class="cardImageWrapper">
<img class="cardImage"
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>
<h2 class="cardTitle">{{block.settings.title}}</h2>
<div class="cardDescription">{{block.settings.card_description}}</div>
</div>
</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">
{% 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}}" onclick="showDot(this.dataset.dotId)">
</li>
{% endfor %}
{% 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}}" onclick="showDot(this.dataset.dotId)">
</li>
{% endfor %}
{% endif %}

{% when 'number' %}
<span class="activeNumber">{{forloop.index}}</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" onclick = "pauseFx()">
<span class="pauseBtn"></span>
<span class="playBtn"> </span>
</button>
{% endif %}
</div>
</div>

{% 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",
"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"
}
]
}
],
"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>"
}
]
}
],
"presets": [
{
"name": "Universal Carousel"
}
]
}
{% endschema %}

3. Next, we need to add a file for our CSS. Go to Asset folder, and click "add a new asset", then click "create a blank file". Make sure you have .css file dropdown on the left. We will name this CSS file "universal-carousel".

4. Open the newly create CSS file and paste the code below

 :root {
--text-color: #232b2b;
--bg-color: #dee2e6;
--card-bg-color: rgba(25, 181, 254, .3);
--box-bg-color: skyblue;
--button-color: rgba(0, 0, 0);
--button-bg-color: rgba(0,0,0,.3);
--button--dark-bg-color: rgba(0,0,0,.6);
}
.carouselContainer {
height: 45vw;
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;
}
.carouselWrapper {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
}
.carouselBox{
width: 100%;
height: 100%;
padding: 3.5% 0;
display: flex;
align-items: center;
margin: 0;
flex-wrap: inherit;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
position: absolute;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.carouselBox::-webkit-scrollbar {
display: none;
}
.carouselSlider {
height: 100%;
width: 25%;
object-fit: contain;
aspect-ratio: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding-right: 2%;
}
/*===== PRODUCT ==== */
.productBox {
background: var(--bg-color);
}
.productBox:hover {
background: var(--box-bg-color);
transform: scale(1.2);
}
.carouselImage {
width: 100%;
aspect-ratio: 1;
}
.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);
white-space: nowrap;
overflow: hidden;
padding: 0 5% 3%;
}
.productPrice {
padding-top: 1%;
font-size: clamp(10px, 2vw ,18px);
font-weight: 600;
}

/* === NUMBER OF SLIDES === */
.three, .four {height: 100%}
.five {height: 60%}
.six, .seven {height: 50%}
.three .carouselSlider {width: 33.5%}
.four .carouselSlider { width: 25%}
.five .carouselSlider { width: 20%}
.six .carouselSlider { width: 16.7%}
.seven .carouselSlider { width: 14.3%}
:is(.six, .seven) .detailsText {font-size: clamp(8px, 1vw ,12px);}

@media screen and (max-width: 800px) {
.carouselContainer {
height: 50vw;
}
.carouselWrapper {
height: 100% !important;
}
.carouselSlider {
height: 100%;
width: 33.5% !important;
}
}
@media screen and (max-width: 400px) {
.carouselContainer {
height: 75vw;
margin: 4%;
}
.carouselBox {
padding: 1.5% 0;
}
.carouselSlider {
padding-left: 4%;
width: 50% !important;
}
.detailsText {
font-size: 2.5vw;
}
.controlContainer:hover {
transform: scale(1.1);
}
#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.2);
}
.cardWrapper:hover .cardImageWrapper {
overflow: visible;
transform: scale(1.2);
}
.cardImageWrapper {
position: relative;
padding-bottom: 65%;
width: 100%;
overflow: hidden;
grid-row: 1 / 2;
}
.cardImage {
width: 100%;
position: absolute;
}
.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;
}
.cardDescription {
width: 100%;
line-height: 1.2;
grid-row: 3 / 4;
}
.cardDescription p {
font-size: clamp(8px, 1.2vw, 18px);
margin: 0;
text-overflow: ellipsis;
}

/* ====== CONTROLS ===== */
.controlContainer {
display: flex;
justify-content: center;
}
.controlContainer:hover {
transform: scale(1.2);
}
.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;
}
.controlBtn {
display: inline-block;
width: 25px;
height: 15px;
background-color: var(--button--dark-bg-color);
border: none;
}
.controlWrapper:hover .controlBtn{
transform: scale(1.2);
background-color: var( --button-color);
}
#nextSlide:disabled .controlBtn {
background-color: var(--button-bg-color);
}
.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: var(--button-bg-color);
}
.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--dark-bg-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--dark-bg-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.2);
background-color: var( --button-color);
}
.pauseBtn:hover {
transform: scale(1.2);
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.2);
}
.slideDots {
position: relative;
background-color: transparent;
border: 2px solid var(--button--dark-bg-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--dark-bg-color);
text-decoration: none;
text-align: center;
}
#viewAllBtn:hover::after {
content: "Products";
}

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

 

5. Sorry, we are not done yet. In the same Asset folder, create a js file. Add a new asset, click the "create a blank file", from the drop down, click the ".js". Name the file "universal-carousel"

6. Open the "universal-carousel.js" file and paste the code below.

const carousel = document.querySelector('.carouselContainer');
const slides = carousel.querySelectorAll('.carouselSlider');
const slideBox = carousel.querySelector('ul#carouselBox');

const interval = slideBox.dataset.interval;
const counter = slideBox.dataset.counterType;
const mediaMedium = window.matchMedia( '( max-width: 800px )' );
const mediaSmall = window.matchMedia( '( max-width: 400px )' );
const slideLot = carousel.querySelector('.activeNumber');

const prevBtn = carousel.querySelector("#prevSlide");
const nextBtn = carousel.querySelector("#nextSlide");
const autoplayBtn = carousel.querySelector('#autoplaySlide');
const slideDots = carousel.querySelectorAll('.slideDots');

const pauseBtn = carousel.querySelector("#autoplaySlide > span.pauseBtn");
const playBtn = carousel.querySelector("#autoplaySlide > span.playBtn");
const dotBtn = carousel.querySelector('.slideDots');

let numSlide = slideBox.dataset.slide;
let infinite = slideBox.dataset.infiniteScroll;
let autoplay = slideBox.dataset.autoplay;
let timeout;
let curSlide = 0;

if (counter == 'number') {
slideLot.innerHTML = 1;
}

if (mediaSmall.matches) {
numSlide = 2;
}else if (mediaMedium.matches) {
numSlide = 3;
} else {
numSlide = slideBox.dataset.slide;
}

const slideWidth = Math.round(slideBox.clientWidth / numSlide);

prevBtn.addEventListener("click", prevFx);
nextBtn.addEventListener("click", nextFx);
goToSlide(0);

if(autoplay == 'true') {
autoPlay();
}else {
clearTimeout(timeout);
}

function autoPlay(numSlide){
nextFx();
timeout = setTimeout(autoPlay, interval);
}

function pauseFx() {
if (autoplay === 'true'){
clearTimeout(timeout);
autoplay = 'false';
playBtn.style.display = "block";
pauseBtn.style.display = "none";
}else{
if(curSlide == slides.length -1) {
curSlide = -1;
}
autoPlay();
autoplay = 'true';
playBtn.style.display = "none";
pauseBtn.style.display = "block";
}
}

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

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

function showDot(dot) {
const clickedDot = carousel.querySelector(`[data-dot-id="${dot}"]`);
slideDots.forEach(dot => dot.classList.remove('activeDot'));
clickedDot.classList.add('activeDot');
if(infinite == 'false') {
if(dot == slides.length - 1) {
nextBtn.setAttribute('disabled', true);
} else {
nextBtn.removeAttribute('disabled');
}
if(dot == 0) {
prevBtn.setAttribute('disabled', true);
} else {
prevBtn.removeAttribute('disabled');
}
}
curSlide = dot;
goToSlide(curSlide);
}

function goToSlide (slide) {
let leftScroll = slide * slideWidth;
slideBox.scrollTo({left: leftScroll});
}

function activeSlide(curSlide) {
if(infinite == 'false') {
if(curSlide == slides.length - 1) {
nextBtn.setAttribute('disabled', true);
} else {
nextBtn.removeAttribute('disabled');
}
if(curSlide == 0) {
prevBtn.setAttribute('disabled', true);
} else {
prevBtn.removeAttribute('disabled');
}
}
if(counter == 'number') {
slideLot.innerHTML = curSlide + 1;
}
if(counter == 'dots') {
showDot(curSlide);
}
}

 

7. Lastly, we have to link our .js file to the theme. Open the Layout folder and click the "theme.liquid". Before the </head>, paste the code below.

  <script src="{{ 'universal-carousel.js' | asset_url }}" defer="defer"></script>

 

You can change the color corresponds to the variables, see code in blue.

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

Featured collection slider for Dawn Theme Shopify 2.0

Add a collection slider for your home page. This only works with Dawn Theme.
Read More

Product Slider for Dawn Theme - No apps or subscript- Just a little bit of code

Add a product slider for Dawn theme, almost like Debut theme slider. No app or subscription needed. No coding experience
Read More

Awesome slideshow for most of Shopify Themes - Work well with Dawn

Awesome slideshow with 11 unique designs to choose from. No app or subscription needed. No coding experience is necessar
Read More

1 comment

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

Tom

Leave a comment