JSFiddle - React, Tailwind, and code Playground
by mehulkar
HTML
<div class="page">
<div class="shelf-grid-body">
<button id="previous" class="shelf-grid-nav"><</button>
<ul id="grid" class="shelf-grid-list">
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
<li><img src="//placecage.com/64/36" width="64" height="36"></li>
</ul>
<button id="next" class="shelf-grid-nav">></button>
</div>
<p><code>overflow:hidden</code> on <code>.shelf-grid-list</code> causes the scrolled items to not be visible. If we remove the overflow-hidden, then the auto-columns don't get created with the right size.</p>
</div>
CSS
body { margin: 0; padding: 0; }
h2 { margin: 0; }
* { box-sizing: border-box; }
.page { width: 90vw; margin: 10px auto 0; }
:root {
--item-gap: 20px;
--items-per-page: 3;
--paddle-nav-width: 20px;
/**
offset value is the grid gap, times the number of times
the grid-gap appears on the page, and then divided by the
number of iems on the page.
*/
--offset: calc(var(--item-gap) * (var(--items-per-page) - 1) / var(--items-per-page));
}
.shelf-grid-body {
display: flex;
width: 100%;
overflow: hidden;
}
.shelf-grid-list {
padding: 0;
margin: 0;
flex-grow: 1;
display: grid;
grid-template-rows: max-content;
grid-auto-columns: calc(100% / var(--items-per-page) - var(--offset));
grid-auto-flow: column;
grid-column-gap: var(--item-gap);
transition-duration: 0.2s;
transition-timing-function: ease-out;
overflow: hidden;
}
li {
list-style: none;
background: bisque;
text-align: center;
border: solid 1px #ccc;
}
.shelf-grid-nav {
background: none;
flex-shrink: 0;
flex-basis: var(--paddle-nav-width);
padding: 0;
cursor: pointer;
border: solid 1px #ccc;
z-index: 1;
background-color: #fff;
}
.shelf-grid-nav:hover { background: #eee; }
.shelf-grid-nav:active { background: #ddd; }
JavaScript
let offset = 0;
let offsetValue = 400;
const prevFn = element => {
offset = offset - offsetValue;
let newValue;
if (offset >= 0) {
newValue = `translateX(-${offset}px);`;
} else {
newValue = 0;
}
element.setAttribute('style', `transform: ${newValue};`);
};
const nextFn = element => {
offset = offset + offsetValue;
const newValue = `translateX(-${offset}px);`;
element.setAttribute('style', `transform: ${newValue};`);
};
const grid = document.querySelector('#grid');
document.querySelector('#previous')
.addEventListener('click', prevFn.bind(null, grid));
document.querySelector('#next')
.addEventListener('click', nextFn.bind(null, grid));