Create an Image Slider using your WordPress Core Blocks

This may be a bit of an unconventional take.

With that out of the way, let’s get to creating a simple slider using the good old core/columns block.

Let’s add a 3 / 3 / 3 columns block:

  • that has stack on mobile unchecked
  • with the inner column block having 100% width

, and add three images from Sri Lanka’s natural beauty.

Wrap it within a group (with class name .columns-image-slider, and wrap that in another group with a an inner width of, say 720px.

Let us add a core HTML block, with the following content in it:

<style>
.columns-image-slider {
  position: relative;
  overflow: hidden;
}
.columns-image-slider .wp-block-columns {
  display: flex;
  flex-wrap: nowrap !important;
  gap: 0 !important;
  margin: 0;
  padding: 0;
  transition: transform 450ms ease;
  will-change: transform;
}
.columns-image-slider .wp-block-column {
  flex: 0 0 100%;
  width: 100%;
  max-width: 100%;
}
.columns-image-slider .wp-block-column > {
  padding: 0 8px;
 box-sizing: border-box;
}
.ts-slider-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-%);
  z-index: 2;
  border: 1px solid currentColor;
  background: rgba(255,255,2559);
  color: inherit;
  padding: .5rem .75rem;
  border-radius: 999px;
  cursor: pointer;
}

.ts-slider-btn.prev{ left: 0; }
.ts-slider-btn.next{ right: 0; }
.ts-slider-dots{
  display: flex;
  gap: .5rem;
  justify-content: center;
  margin-top: .75rem;
}
.ts-slider-dot{
  width: 10px;
  height: 10px;
  border-radius: 999px;
  border: 1px solid currentColor;
  background: transparent;
  cursor: pointer;
  opacity: .7;
}

.ts-slider-dot[aria-current="true"]{
  background: currentColor;
  opacity: 1;
}
@media (prefers-reduced-motion: reduce){
  .columns-image-slider .wp-block-columns{ transition: none; }
}
</style>

<script>
(() => {
  const carousels = document.querySelectorAll('.columns-image-slider');

  carousels.forEach((root) => {
    const track = root.querySelector('.wp-block-columns');
    if (!track) return;

    const slides = Array.from(track.querySelectorAll('.wp-block-column'));
    if (slides.length <= 1) return;

    // Ensure initial state
    let index = 0;

    // Build controls
    const prev = document.createElement('button');
    prev.className = 'ts-slider-btn prev';
    prev.type = 'button';
    prev.setAttribute('aria-label', 'Previous testimonial');
    prev.textContent = '‹';

    const next = document.createElement('button');
    next.className = 'ts-slider-btn next';
    next.type = 'button';
    next.setAttribute('aria-label', 'Next testimonial');
    next.textContent = '›';

    root.appendChild(prev);
    root.appendChild(next);

    // Dots (insert after carousel so it sits below)
    const dotsWrap = document.createElement('div');
    dotsWrap.className = 'ts-slider-dots';

    const dots = slides.map((_, i) => {
      const dot = document.createElement('button');
      dot.className = 'ts-slider-dot';
      dot.type = 'button';
      dot.setAttribute('aria-label', `Go to testimonial ${i + 1}`);
      dot.addEventListener('click', () => goTo(i));
      dotsWrap.appendChild(dot);
      return dot;
    });

    root.insertAdjacentElement('afterend', dotsWrap);

    function update() {
      track.style.transform = `translateX(${-index * 100}%)`;
      dots.forEach((d, i) => d.setAttribute('aria-current', i === index ? 'true' : 'false'));
    }

    function goTo(i) {
      index = (i + slides.length) % slides.length;
      update();
    }

    prev.addEventListener('click', () => goTo(index - 1));
    next.addEventListener('click', () => goTo(index + 1));

    // Basic swipe support
    let startX = null;
    track.addEventListener('pointerdown', (e) => {
      startX = e.clientX;
      track.setPointerCapture?.(e.pointerId);
    });

    track.addEventListener('pointerup', (e) => {
      if (startX == null) return;
      const dx = e.clientX - startX;
      startX = null;
      if (Math.abs(dx) < 40) return;
      if (dx < 0) goTo(index + 1);
      else goTo(index - 1);
    });

    // Keyboard focus support
    root.setAttribute('tabindex', '0');
    root.addEventListener('keydown', (e) => {
      if (e.key === 'ArrowLeft') goTo(index - 1);
      if (e.key === 'ArrowRight') goTo(index + 1);
    });

    update();
  });
})();
</script>

This is what you end up with:

Perhaps this is not a lot, but can be a simple yet effective way to put up testimonials without requiring a whole new plugin (oh!) or come up with experiments on the same page like this.

Thank you!

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *