Swiper Slider Setup

A responsive draggable slider built with the Swiper library, featuring breakpoint-based slides-per-view, optional prev/next buttons, dot pagination, mousewheel support, and keyboard navigation.

sliderswiperresponsivebreakpointspaginationkeyboardmousewheelcms

Setup — External Scripts

Swiper 12 CSS
html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css"/>
Swiper 12 JS
html
<script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>

Code

index.html
html
<div data-swiper-group class="swiper-group">
  <div data-swiper-wrap class="swiper">
    <div class="swiper-wrapper">
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 1</span></div>
        </div>
      </div>
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 2</span></div>
        </div>
      </div>
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 3</span></div>
        </div>
      </div>
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 4</span></div>
        </div>
      </div>
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 5</span></div>
        </div>
      </div>
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 6</span></div>
        </div>
      </div>
      <div class="swiper-slide">
        <div class="demo-card">
          <div class="demo-card__visual"></div>
          <div class="demo-card__text"><span class="demo-card__title">Slide 7</span></div>
        </div>
      </div>
    </div>
  </div>
  <div class="swiper-navigation">
    <button data-swiper-prev="" class="swiper-navigation__button">
      <svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none" class="swiper-navigation__button-arorw is--prev">
        <path d="M14 19L21 12L14 5" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></path>
        <path d="M21 12H2" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></path>
      </svg>
    </button>
    <div class="swiper-pagination"></div>
    <button data-swiper-next="" class="swiper-navigation__button">
      <svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none" class="swiper-navigation__button-arorw">
        <path d="M14 19L21 12L14 5" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></path>
        <path d="M21 12H2" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></path>
      </svg>
    </button>
  </div>
</div>
styles.css
css
body {
  --swiper-pagination-color: currentColor;
  --swiper-pagination-bottom: auto;
  --swiper-pagination-bullet-size: 0.5em;
  --swiper-pagination-bullet-inactive-color: currentColor;
  --swiper-pagination-bullet-inactive-opacity: 0.15;
  --swiper-pagination-bullet-horizontal-gap: 0.25em;
  --swiper-wrapper-transition-timing-function: cubic-bezier(0.625, 0.05, 0, 1);
}

.swiper-group {
  width: 100%;
  position: relative;
}

.swiper {
  flex-flow: row;
  justify-content: flex-start;
  align-items: flex-start;
  width: 100%;
  display: flex;
  overflow: visible !important;
}

.swiper-wrapper {
  flex-flow: row;
  justify-content: flex-start;
  align-items: flex-start;
  width: 100%;
  display: flex;
}

.swiper-slide {
  flex: none;
  max-width: 20em;
  --gap: 1.25em;
  padding-right: var(--gap);
}

.swiper-slide:last-of-type {
  margin-right: calc(-1 * var(--gap));
}

.swiper-pagination {
  pointer-events: auto;
  z-index: 0 !important;
}

.swiper-navigation {
  z-index: 2;
  pointer-events: none;
  justify-content: space-between;
  align-items: center;
  display: flex;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  transform: translate(0, 150%);
}

.swiper-navigation__button {
  z-index: 1;
  aspect-ratio: 1;
  pointer-events: auto;
  color: #000;
  background-color: #fff;
  border-radius: 100em;
  justify-content: center;
  align-items: center;
  width: 3em;
  padding: 1em;
  display: flex;
  position: relative;
  transition: opacity 0.2s ease;
}

.swiper-button-disabled {
  opacity: 0;
  pointer-events: none;
}

.swiper-navigation__button-arorw {
  justify-content: center;
  align-items: center;
  width: 100%;
  display: flex;
}

.swiper-navigation__button-arorw.is--prev {
  transform: rotate(-180deg);
}

.demo-card {
  aspect-ratio: 4 / 5.25;
  background-color: #131313;
  border: 1px solid #ffffff26;
  border-radius: 1em;
  flex-flow: column;
  justify-content: space-between;
  align-items: stretch;
  width: 100%;
  padding: 1em;
  display: flex;
}

.demo-card__visual {
  background-color: #ffffff08;
  background-image: linear-gradient(135deg, #ffffff08, #ffffff14 11%, #ffffff08 16%, #ffffff12 58%, #ffffff17 63%, #ffffff08 73%, #ffffff0d 96%, #ffffff08);
  border: 1px solid #ffffff0d;
  border-radius: .5em;
  flex: 1;
}

.demo-card__text {
  padding-top: 1em;
  padding-bottom: .25em;
  padding-left: .5em;
}

.demo-card__title {
  font-size: 1.5em;
}
script.js
javascript
function initSwiperSlider() {
  const swiperSliderGroups = document.querySelectorAll("[data-swiper-group]");

  swiperSliderGroups.forEach((swiperGroup) => {
    const swiperSliderWrap = swiperGroup.querySelector("[data-swiper-wrap]");
    if (!swiperSliderWrap) return;

    const prevButton = swiperGroup.querySelector("[data-swiper-prev]");
    const nextButton = swiperGroup.querySelector("[data-swiper-next]");

    const swiper = new Swiper(swiperSliderWrap, {
      slidesPerView: 1.25,
      speed: 600,
      mousewheel: true,
      grabCursor: true,
      breakpoints: {
        // when window width is >= 480px
        480: {
          slidesPerView: 1.8,
        },
        // when window width is >= 992px
        992: {
          slidesPerView: 3.5,
        }
      },
      navigation: {
        nextEl: nextButton,
        prevEl: prevButton,
      },
      pagination: {
        el: '.swiper-pagination',
        type: 'bullets',
        clickable: true
      },
      keyboard: {
        enabled: true,
        onlyInViewport: false,
      },
    });
  });
}

// Initialize Swiper Slider Setup
document.addEventListener('DOMContentLoaded', () => {
  initSwiperSlider();
});

Attributes

NameTypeDefaultDescription
data-swiper-groupstring""Add to the outermost wrapper that contains the slider, navigation buttons, and pagination. The script queries for [data-swiper-wrap], [data-swiper-prev], and [data-swiper-next] scoped to this element.
data-swiper-wrapstring""Add to the element that Swiper initializes on. Must also have the class .swiper — the class is required by Swiper's API, the attribute is used for easy JS targeting.
data-swiper-prevstring""Add to the previous button inside [data-swiper-group]. Swiper automatically applies .swiper-button-disabled when the first slide is active.
data-swiper-nextstring""Add to the next button inside [data-swiper-group]. Swiper automatically applies .swiper-button-disabled when there are no more slides to advance to.

Notes

  • The .swiper class on the slider element is required by the Swiper library to function — do not remove it.
  • The .swiper-wrapper class on the direct child of the slider and .swiper-slide on each slide are also required by Swiper.
  • Slide gaps are created with padding-right on .swiper-slide rather than Swiper's spaceBetween option, which only accepts pixels. A negative margin-right on the last slide removes the trailing gap.
  • Swiper's breakpoints work in reverse from typical CSS media queries — the top-level slidesPerView is the default for the smallest screens, and breakpoints increase from there.
  • CSS custom properties on body control Swiper's pagination dot appearance (color, size, inactive opacity, gap). Override these per-instance by scoping them to a parent selector.
  • For Webflow CMS: give the Collection Wrap .swiper and [data-swiper-wrap], the Collection List .swiper-wrapper, and each Collection Item .swiper-slide.
  • The swiper-navigation div uses pointer-events: none so clicks pass through to the slider; individual buttons re-enable pointer-events to remain clickable.

Guide

Group, Swiper & Wrapper

Wrap everything in [data-swiper-group]. Inside it, add [data-swiper-wrap] with class .swiper (both required). The direct child of the swiper element must have class .swiper-wrapper — this is Swiper's required DOM structure.

Slides

Each slide needs the class .swiper-slide. You can set a max-width in CSS; the actual rendered width is controlled by slidesPerView in the JS config. Place your card or content element inside the slide wrapper.

Slide gap

Use the --gap CSS custom property with padding-right on .swiper-slide to set spacing in any CSS unit. Apply margin-right: calc(-1 * var(--gap)) to .swiper-slide:last-of-type to eliminate the trailing gap at the end of the track.

Slides per view & breakpoints

Set the default (mobile-first) slidesPerView at the top level of the Swiper config, then add breakpoints for larger screens. Each breakpoint key is a minimum viewport width in pixels.

slidesPerView: 1.25,
breakpoints: {
  480: { slidesPerView: 1.8 },
  992: { slidesPerView: 3.5 }
}

Navigation buttons (optional)

Add two buttons anywhere inside [data-swiper-group] with [data-swiper-prev] and [data-swiper-next]. Swiper applies .swiper-button-disabled automatically — style that class to hide or fade buttons at the ends of the track.

Pagination dots (optional)

Add a div with class .swiper-pagination anywhere inside [data-swiper-group]. Swiper fills it with bullet elements automatically. Control their appearance via the CSS custom properties on body (--swiper-pagination-color, --swiper-pagination-bullet-size, etc.).

Webflow CMS

To drive slides from a CMS collection: give the Collection Wrap .swiper and [data-swiper-wrap], the Collection List .swiper-wrapper, and each Collection Item .swiper-slide. No changes to the JS are required.