Hello. Does anyone know how can I make to display dots or lines instead of 1/x?
Topic summary
Users are trying to replace the default “1/x” numeric pagination indicator with dots or lines in the Dawn theme’s product image gallery.
Initial Suggestion:
- One user recommended navigating to Theme Customize → Product pages → Image navigation style and selecting “Dots” or “Lines” from a dropdown menu.
- However, multiple users confirmed this option doesn’t exist in their Dawn theme installations.
Working Solutions Provided:
-
JavaScript modification approach (post #8):
- Edit
Assets/global.jsto replace theSliderComponentclass - Modify
Sections/main-product.liquidto replace the counter markup with custom dot navigation - Links to another community thread with detailed code
- Edit
-
Liquid template approach (post #10):
- Add CSS and JavaScript directly to
product-media-gallery.liquidormain-product.liquid - Creates clickable dots that sync with slide navigation
- Includes a checkbox setting (
enable_dots) to toggle between dots and default numbers - Provides complete code snippets for styles, scripts, and section settings
- Add CSS and JavaScript directly to
Status: The discussion appears resolved with two technical solutions available, though implementation requires theme code editing.
Hello @AndreiGhetu ,
You can try to do this:
- Go to Online Store → Theme → Customize
- In the left-hand panel, click on the “Product pages” section. → product image gallery → Image navigation style
- Choose “Dots” or “Lines” from the drop-down menu to display dots or lines instead of counting numbers.
Hope this can help. Let us know if you need anything else.
Regards,
Ali Reviews team.
Hi. Thank you for your reply, but I can’t find any “Product pages” section.
Hello @AndreiGhetu ,
You can find it like in my screenshot.
Let us know if you need anything else.
Ali Reviews team.
I found it but I still can’t find “Image navigation style”, and I don’t have any drop-down menu to chose between dots and lines.
Hey Andrei! Did you find any solution? Im stuck in the same place…
I’m also stuck in this. Please can somebody help?
I found the answer in this thread: https://community.shopify.com/post/1893507
I’ll duplicate the answer here:
-
Go to Store Online → theme → edit code
-
Assets/global.js
-
find class class SliderComponent after that replace with code below
class SliderComponent extends HTMLElement {
constructor() {
super();
this.slider = this.querySelector('[id^="Slider-"]');
this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
this.enableSliderLooping = false;
this.currentPageElement = this.querySelector('.slider-counter--current');
this.pageTotalElement = this.querySelector('.slider-counter--total');
this.prevButton = this.querySelector('button[name="previous"]');
this.nextButton = this.querySelector('button[name="next"]');
if (!this.slider || !this.nextButton) return;
this.initPages();
const resizeObserver = new ResizeObserver(entries => this.initPages());
resizeObserver.observe(this.slider);
this.slider.addEventListener('scroll', this.update.bind(this));
this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
this.nextButton.addEventListener('click', this.onButtonClick.bind(this));
this.sliderControlLinksArray = Array.from(this.querySelectorAll('.slider-counter__link'));
this.sliderControlLinksArray.forEach(link => link.addEventListener('click', this.linkToSlide.bind(this)));
}
initPages() {
this.sliderItemsToShow = Array.from(this.sliderItems).filter(element => element.clientWidth > 0);
if (this.sliderItemsToShow.length < 2) return;
this.sliderItemOffset = this.sliderItemsToShow[1].offsetLeft - this.sliderItemsToShow[0].offsetLeft;
this.slidesPerPage = Math.floor((this.slider.clientWidth - this.sliderItemsToShow[0].offsetLeft) / this.sliderItemOffset);
this.totalPages = this.sliderItemsToShow.length - this.slidesPerPage + 1;
this.update();
}
resetPages() {
this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
this.initPages();
}
update() {
const previousPage = this.currentPage;
this.currentPage = Math.round(this.slider.scrollLeft / this.sliderItemOffset) + 1;
if (this.currentPageElement && this.pageTotalElement) {
this.currentPageElement.textContent = this.currentPage;
this.pageTotalElement.textContent = this.totalPages;
}
if (this.currentPage != previousPage) {
this.dispatchEvent(new CustomEvent('slideChanged', { detail: {
currentPage: this.currentPage,
currentElement: this.sliderItemsToShow[this.currentPage - 1]
}}));
}
if (this.enableSliderLooping) return;
if (this.isSlideVisible(this.sliderItemsToShow[0]) && this.slider.scrollLeft === 0) {
this.prevButton.setAttribute('disabled', 'disabled');
} else {
this.prevButton.removeAttribute('disabled');
}
if (this.isSlideVisible(this.sliderItemsToShow[this.sliderItemsToShow.length - 1])) {
this.nextButton.setAttribute('disabled', 'disabled');
} else {
this.nextButton.removeAttribute('disabled');
}
this.sliderControlButtons = this.querySelectorAll('.slider-counter__link');
if (!this.sliderControlButtons.length) return;
this.sliderControlButtons.forEach(link => {
link.classList.remove('slider-counter__link--active');
link.removeAttribute('aria-current');
});
this.sliderControlButtons[this.currentPage - 1].classList.add('slider-counter__link--active');
this.sliderControlButtons[this.currentPage - 1].setAttribute('aria-current', true);
}
isSlideVisible(element, offset = 0) {
const lastVisibleSlide = this.slider.clientWidth + this.slider.scrollLeft - offset;
return (element.offsetLeft + element.clientWidth) <= lastVisibleSlide && element.offsetLeft >= this.slider.scrollLeft;
}
onButtonClick(event) {
event.preventDefault();
const step = event.currentTarget.dataset.step || 1;
this.slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollLeft + (step * this.sliderItemOffset) : this.slider.scrollLeft - (step * this.sliderItemOffset);
this.slider.scrollTo({
left: this.slideScrollPosition
});
}
linkToSlide(event) {
event.preventDefault();
const slideScrollPosition = this.slider.scrollLeft + this.sliderItemOffset * (this.sliderControlLinksArray.indexOf(event.currentTarget) + 1 - this.currentPage);
this.slider.scrollTo({
left: slideScrollPosition
});
}
}
-
Sections/main-product.liquid
-
find code below
1
/
{{ 'general.slider.of' | t }}
{{ media_count }}
- Replace code of step 5 with code below
{%- assign featured_media = product.selected_or_first_available_variant.featured_media -%}
{% if section.settings.hide_variants and variant_images contains featured_media.src %}
{% endif %}
{%- for media in product.media -%}
{%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
{% if section.settings.hide_variants and variant_images contains media.src %}
{% else %}
{%- endif -%}
{%- endunless -%}
{%- endfor -%}
Add This code in product-media-gallery.liquid on on main-product.liquid
{% if section.settings.enable_dots %}
<style>
.slider-dots {
display: flex;
gap: 6px;
justify-content: center;
margin-top: 8px;
}
.slider-dots .dot {
width: 12px;
height: 12px;
background: #ccc;
border-radius: 50%;
cursor: pointer;
}
.slider-dots .dot.active {
background: #000;
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('slider-component').forEach(function (slider) {
const dots = slider.querySelectorAll('.slider-dots .dot');
const slideTrack = slider.querySelector('.slider'); // UL with slides
const slides = slider.querySelectorAll('.slider__slide');
const currentCounter = slider.querySelector('.slider-counter--current');
function setActiveDot(index) {
dots.forEach((dot, i) => {
dot.classList.toggle('active', i === index);
});
if (currentCounter) currentCounter.textContent = index + 1;
}
function goToSlide(index) {
const targetSlide = slides[index];
if (!targetSlide) return;
slideTrack.scrollTo({
left: targetSlide.offsetLeft,
behavior: 'smooth'
});
}
// Dot clicks
dots.forEach((dot, i) => {
dot.addEventListener('click', () => {
goToSlide(i);
setActiveDot(i);
});
});
// Button clicks
const nextBtn = slider.querySelector('.slider-button--next');
const prevBtn = slider.querySelector('.slider-button--prev');
let activeIndex = 0;
function updateFromButtons(dir) {
activeIndex = (activeIndex + dir + slides.length) % slides.length;
goToSlide(activeIndex);
setActiveDot(activeIndex);
}
if (nextBtn) nextBtn.addEventListener('click', () => updateFromButtons(1));
if (prevBtn) prevBtn.addEventListener('click', () => updateFromButtons(-1));
// Swipe / grab scroll listener
let scrollTimeout;
slideTrack.addEventListener('scroll', function () {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
let closestIndex = 0;
let closestDistance = Infinity;
slides.forEach((slide, i) => {
const distance = Math.abs(slide.offsetLeft - slideTrack.scrollLeft);
if (distance < closestDistance) {
closestDistance = distance;
closestIndex = i;
}
});
activeIndex = closestIndex;
setActiveDot(activeIndex);
}, 100); // debounce for smoother update
});
});
});
</script>
{% endif %}
Replace code in product-media-gallery
Replace this
<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>
with
{% if section.settings.enable_dots %}<div class="slider-dots" id="SliderDots-{{ section.id }}">
{% for i in (1..media_count) %}
<span class="dot{% if forloop.first %} active{% endif %}" data-slide="{{ forloop.index0 }}"></span>
{% endfor %}
</div>
{% else %}
<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>
{% endif %}
Now add checkbox in section settings:
{
"type": "checkbox",
"id": "enable_dots",
"default": false,
"label": "Enable dots",
"info": "Else it will show numbers and Arrows"
},
Hello @AndreiGhetu
Okay, sure. Please send me the collaborator code, and I will check and update you with the proper solution.


