CSS Snap Scroll Slider
Minimal JS Carousel with CSS Snap Scroll
by ninty9notout
HTML
<link rel="stylesheet" href="https://unpkg.com/modern-css-reset/dist/reset.min.css">
<script src="https://fonts.googleapis.com/css2?family=Bree+Serif&family=PT+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap"></script>
<div class="section">
<h2 class="section__heading">
At Vero Eos et Accusamus
</h2>
<div class="heading__slider slider" data-controls>
<div class="slider__list">
<div class="slider__item">
<a href="https://www.example.com" class="card">
<div class="card__media">
<img src="https://picsum.photos/640/360?1" alt="Et harum quidem rerum" class="card__image">
</div>
<div class="card__body">
<h3 class="card__title">
Et harum quidem rerum
</h3>
<p class="card__description">
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium
</p>
</div>
</a>
</div>
<div class="slider__item">
<a href="https://www.example.com" class="card">
<div class="card__media">
<img src="https://picsum.photos/640/360?2" alt="Et harum quidem rerum" class="card__image">
</div>
<div class="card__body">
<h3 class="card__title">
Et harum quidem rerum
</h3>
<p class="card__description">
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium
</p>
</div>
</a>
</div>
<div class="slider__item">
<a...
SCSS
html {
font-family: 'PT Sans', sans-serif;
font-size: 16px;
line-height: 1rem;
}
body {
background-color: #EAEAEA;
color: #333333;
}
.section {
padding: 20px;
}
.section__heading {
font-family: 'Bree Serif', serif;
font-size: 2.5rem;
line-height: 1;
margin-bottom: 10px;
&::after {
content: '';
height: 5px;
background-color: #333333;
width: 20%;
display: block;
margin-top: 10px;
}
}
.slider {
overflow: hidden;
margin-left: -20px;
margin-right: -20px;
position: relative;
}
.slider__list {
display: flex;
flex-wrap: nowrap;
overflow-x: scroll;
scroll-snap-type: x mandatory;
scroll-padding-left: 10px;
@media (min-width: 1281px) {
overflow-x: hidden;
scroll-snap-type: initial;
scroll-padding-left: initial;
}
&::before, &::after {
content: '';
display: block;
min-width: 10px;
height: 1px;
}
}
.slider__item {
padding: 10px;
scroll-snap-align: start;
scroll-snap-stop: always;
@media (min-width: 1281px) {
scroll-snap-align: initial;
}
}
.slider__controls {
position: absolute;
left: 0;
top: 10px;
right: 0;
bottom: 10px;
display: none;
justify-content: space-between;
align-items: center;
pointer-events: none;
opacity: .1;
transition: opacity 250ms ease;
@media (min-width: 1281px) {
display: flex;
}
.slider:hover & {
opacity: 1;
}
}
.slider__control {
all: unset;
cursor: pointer;
height: 100%;
font-size: 10rem;
padding-left: 2rem;
padding-right: 2rem;
pointer-events: all;
}
.slider__control--hidden {
pointer-events: none;
opacity: 0;
}
.slider__control--previous {
background: linear-gradient(90deg, rgba(234, 234, 234, .75) 0%, rgba(234, 234, 234, 0) 100%);
}
.slider__control--next {
background:...
JavaScript
(function () {
const sliderSelector = '.slider';
const sliderListSelector = '.slider__list';
const sliderItemSelector = '.slider__item';
const controlsSelector = '.slider__controls';
const controlSelector = '.slider__control';
const determineBoolean = value => {
if (String(value).toLowerCase() === 'true') {
return true;
}
if (Number(value) > 0 || Number(value) < 0) {
return true;
}
return false;
}
const determinePages = slider => {
const totalSlides = slider.querySelectorAll(sliderItemSelector).length;
const slideWidth = slider.querySelector(sliderItemSelector).offsetWidth;
const trackWidth = slideWidth * totalSlides;
const slidesPerPage = Math.floor(slider.offsetWidth / slideWidth);
const pageWidth = slidesPerPage * slideWidth;
const totalPages = Math.ceil(trackWidth / pageWidth);
return [...Array(totalPages).keys()].map((page, index) => {
return index * pageWidth;
});
}
const generateControls = slider => {
const controls = document.createElement('div');
controls.classList.add(controlsSelector.replace('.', ''));
const previousButton = document.createElement('button');
previousButton.classList.add(controlSelector.replace('.', ''));
previousButton.classList.add(`${controlSelector.replace('.', '')}--previous`);
previousButton.innerHTML = '‹';
const nextButton = document.createElement('button');
nextButton.classList.add(controlSelector.replace('.', ''));
nextButton.classList.add(`${controlSelector.replace('.', '')}--next`);
nextButton.innerHTML = '›';
controls.appendChild(previousButton);
controls.appendChild(nextButton);
slider.appendChild(controls);
return { previousButton, nextButton };
}
...