Custom Autoplay Slider Progress Buttons in Kadence
Home » News » Custom Autoplay Slider Progress Buttons in Kadence

Custom Autoplay Slider Progress Buttons in Kadence

If you want custom slider buttons in Kadence that show a progress fill for each slide, here’s the quick version of how I did it.

Kadence sliders run on Splide, so instead of fighting the built-in autoplay, I turned autoplay off and built my own. I added three simple buttons (Slide 1 / Slide 2 / Slide 3) under the slider. Each button has a CSS animation that fills the background like a progress bar.

Then a small JS script “clicks” the hidden Splide dots behind the scenes to move the slider. When a new slide starts, the progress bar resets and the next button begins filling. Clicking a button manually jumps to that slide and restarts the timing.

So in short:
Custom buttons → Buttons animate → JS triggers Splide dots → Slider stays in sync.

This gives you clean buttons, a visible timing bar, and way more control than the default autoplay. Code below that can be dropped above the Kadence Advanced slider in a Custom HTML block:

<div class="custom-slider-nav-buttons">
  <button type="button" class="progress-btn" data-slide="0"><span>Slide 1</span></button>
  <button type="button" class="progress-btn" data-slide="1"><span>Slide 2</span></button>
  <button type="button" class="progress-btn" data-slide="2"><span>Slide 3</span></button>
</div>

<script>
document.addEventListener('DOMContentLoaded', function () {
  const SLIDE_DURATION = 5000;
  const slider  = document.querySelector('.custom-slider-nav .splide');
  const buttons = Array.from(document.querySelectorAll('.custom-slider-nav-buttons .progress-btn'));
  if (!slider || !buttons.length) return;

  let current = 0;
  let timer = null;

  function setActive(index) {
    current = index;
    buttons.forEach(btn => { btn.classList.remove('active'); void btn.offsetWidth; });
    const activeBtn = buttons[index];
    if (activeBtn) {
      activeBtn.style.setProperty('--slide-duration', SLIDE_DURATION + 'ms');
      activeBtn.classList.add('active');
    }
    const dots = slider.querySelectorAll('.splide__pagination__page');
    if (dots[index]) dots[index].click();
  }

  function startLoop() {
    if (timer) clearInterval(timer);
    setActive(current);
    timer = setInterval(() => {
      current = (current + 1) % buttons.length;
      setActive(current);
    }, SLIDE_DURATION);
  }

  buttons.forEach((btn, i) => btn.addEventListener('click', () => { current = i; startLoop(); }));
  buttons.forEach(btn => {
    btn.addEventListener('mouseenter', () => { if (timer) clearInterval(timer); });
    btn.addEventListener('mouseleave', startLoop);
  });

  startLoop();
});
</script>

<style>
.custom-slider-nav-buttons{display:flex;gap:.5rem;justify-content:center;margin-top:1rem;}
.progress-btn{position:relative;padding:.5rem 1.25rem;border-radius:999px;border:none;background:#1e293b;color:#fff;font-size:.95rem;cursor:pointer;overflow:hidden;}
.progress-btn span{position:relative;z-index:2;}
.progress-btn::before{content:"";position:absolute;inset:0;background:#38bdf8;transform-origin:left;transform:scaleX(0);z-index:1;}
.progress-btn.active::before{animation:buttonFill var(--slide-duration,5000ms) linear forwards;}
@keyframes buttonFill{from{transform:scaleX(0);}to{transform:scaleX(1);}}
</style>

Pointers

  • Turn autoplay OFF in Kadence — this code handles it instead.
  • Leave dots ON — the script clicks them behind the scenes to change slides.
  • You can rename the buttons however you want (icons, text, numbers).
  • Timing is controlled by SLIDE_DURATION — change one value and everything syncs.
  • Works in any Kadence Advanced Slider block as long as you wrap it in .custom-slider-nav.

This is a demo so the number of buttons are hardcoded, you can change it manually to match or the code can be adapted to automatically do this too!