<div class="gallery-wrapper">
<ul class="thumb-list">
<li class="is-active">
<img width="1920" height="1280" src="https://assets.codepen.io/162656/sports-car1.jpg" alt="">
</li>
<li>
<img width="1920" height="1280" src="https://assets.codepen.io/162656/sports-car2.jpg" alt="">
</li>
<li>
<img width="1920" height="1280" src="https://assets.codepen.io/162656/sports-car3.jpg" alt="">
</li>
<li>
<img width="1920" height="1280" src="https://assets.codepen.io/162656/sports-car4.jpg" alt="">
</li>
</ul>
<ul class="featured-list">
<li class="is-active">
<div class="featured-img" style="background-image: url(https://assets.codepen.io/162656/sports-car1.jpg); width: 1920px; height: 1280px;"></div>
</li>
<li>
<div class="featured-img" style="background-image: url(https://assets.codepen.io/162656/sports-car2.jpg); width: 1920px; height: 1280px;"></div>
</li>
<li>
<div class="featured-img" style="background-image: url(https://assets.codepen.io/162656/sports-car3.jpg); width: 1920px; height: 1280px;"></div>
</li>
<li>
<div class="featured-img" style="background-image: url(https://assets.codepen.io/162656/sports-car4.jpg); width: 1920px; height: 1280px;"></div>
</li>
</ul>
<span class="notification">Drag!</span>
<button type="button" class="open-lightbox">Open Lightbox</button>
</div>
<div class="lightbox">
<header class="lightbox-header">
<button type="button" class="close-lightbox" aria-label="Close lightbox">✕</button>
</header>
<div class="lightbox-dialog">
<section class="lightbox-content">
<ul class="lightbox-items">
<li>
<img src="https://assets.codepen.io/162656/sports-car1.jpg" alt="" width="1920" height="1280">
</li>
<li>
<img src="https://assets.codepen.io/162656/sports-car2.jpg" alt="" width="1920" height="1280">
</li>
<li>
<img src="https://assets.codepen.io/162656/sports-car3.jpg" alt="" width="1920" height="1280">
</li>
<li>
<img src="https://assets.codepen.io/162656/sports-car4.jpg" alt="" width="1920" height="1280">
</li>
</ul>
<button type="button" class="lightbox-control lightbox-control-next" aria-label="Next slide">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="34" height="34" viewBox="0 0 24 24">
<path d="M5 3l3.057-3 11.943 12-11.943 12-3.057-3 9-9z" />
</svg>
</button>
<button type="button" class="lightbox-control lightbox-control-prev" aria-label="Previous slide">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="34" height="34" viewBox="0 0 24 24">
<path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z" />
</svg>
</button>
</section>
</div>
</div>
<footer class="page-footer">
<span>made by </span>
<a href="https://georgemartsoukos.com/" target="_blank">
<img width="24" height="24" src="https://assets.codepen.io/162656/george-martsoukos-small-logo.svg" alt="George Martsoukos logo">
</a>
</footer>
/* RESET STYLES & HELPER CLASSES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
:root {
--red: red;
--white: #fff;
--black: #333;
--darkcyan: #2a9d8f;
--hovered-thumb: rgba(139, 216, 198, 0.35);
--lightbox-header: rgba(0, 0, 0, 0.3);
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
button {
cursor: pointer;
color: inherit;
background: transparent;
border: none;
outline: none;
font-size: inherit;
}
img {
max-width: 100%;
height: auto;
}
ul {
list-style: none;
}
body {
margin: 30px 0;
color: var(--white);
font: 20px / 1.2 sans-serif;
}
.overflow-y-hidden {
overflow-y: hidden;
}
/* GALLERY STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.gallery-wrapper {
position: relative;
max-width: 950px;
padding: 0 15px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 3fr;
grid-gap: 15px;
}
.gallery-wrapper .thumb-list {
display: grid;
grid-gap: 15px;
}
.gallery-wrapper .thumb-list li {
position: relative;
cursor: pointer;
border: 4px solid var(--black);
}
.gallery-wrapper .thumb-list li:not(.is-active):hover::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--hovered-thumb);
}
.gallery-wrapper .thumb-list li.is-active {
border-color: var(--red);
}
.gallery-wrapper .thumb-list img {
display: block;
}
.gallery-wrapper .featured-list {
position: relative;
border: 4px solid var(--black);
overflow: hidden;
}
.gallery-wrapper .featured-list li {
opacity: 0;
transition: opacity 0.25s;
}
.gallery-wrapper .featured-list li.is-active {
opacity: 1;
}
.gallery-wrapper .featured-list .featured-img {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
z-index: 1 !important;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.gallery-wrapper .notification,
.gallery-wrapper .open-lightbox {
position: absolute;
top: 10px;
right: 30px;
padding: 5px;
background: var(--darkcyan);
z-index: 1;
}
.gallery-wrapper .open-lightbox {
right: 100px;
}
@media (max-width: 750px) {
.gallery-wrapper {
grid-template-columns: 1fr;
}
.gallery-wrapper .thumb-list {
grid-template-columns: repeat(4, 1fr);
order: 1;
}
.gallery-wrapper .featured-list {
height: 340px;
}
}
/* LIGHTBOX STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.lightbox {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
opacity: 0;
visibility: hidden;
z-index: 2;
transition: all 0.25s;
background: var(--black);
}
.lightbox.is-visible {
opacity: 1;
visibility: visible;
}
.lightbox-header {
position: absolute;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: flex-end;
padding: 5px 10px;
z-index: 2;
background: var(--lightbox-header);
}
.lightbox-header .close-lightbox {
font-size: 1.8rem;
}
.lightbox-dialog {
display: flex;
align-items: center;
}
.lightbox-content {
position: relative;
}
.lightbox-items {
display: grid;
}
.lightbox-items li {
display: flex;
grid-column: 1;
grid-row: 1;
opacity: 0;
transition: opacity 0.25s;
}
.lightbox-items li.is-active {
opacity: 1;
}
.lightbox-items img {
width: auto;
max-height: 100vh;
}
.lightbox-control {
position: absolute;
top: 50%;
transform: translateY(-50%);
padding: 4px;
background: var(--darkcyan);
}
.lightbox-control-prev {
left: 0;
}
.lightbox-control-next {
right: 0;
}
.lightbox-control svg {
display: block;
fill: var(--white);
}
/* FOOTER STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.page-footer {
position: fixed;
right: 15px;
bottom: 20px;
display: flex;
align-items: center;
font-size: 1rem;
padding: 5px;
color: var(--black);
z-index: 1;
}
.page-footer a {
display: flex;
margin-left: 4px;
}
const body = document.body;
const galleryWrapper = document.querySelector(".gallery-wrapper");
const thumbList = galleryWrapper.querySelector(".thumb-list");
const thumbItems = thumbList.querySelectorAll("li");
const featuredList = galleryWrapper.querySelector(".featured-list");
const featuredItems = featuredList.querySelectorAll("li");
const featuredImgs = featuredList.querySelectorAll(".featured-img");
const openLightbox = galleryWrapper.querySelector(".open-lightbox");
const lightbox = document.querySelector(".lightbox");
const lightboxItems = lightbox.querySelector(".lightbox-items");
const lightboxControls = lightbox.querySelectorAll(".lightbox-control");
const lightboxPrevControl = lightbox.querySelector(".lightbox-control-prev");
const lightboxNextControl = lightbox.querySelector(".lightbox-control-next");
const closeLightbox = lightbox.querySelector(".close-lightbox");
const isActiveClass = "is-active";
const isVisibleClass = "is-visible";
const overflowYHiddenClass = "overflow-y-hidden";
/* IMAGE GALLERY
–––––––––––––––––––––––––––––––––––––––––––––––––– */
thumbItems.forEach((el) => {
el.addEventListener("click", () => {
thumbList.querySelector("li.is-active").classList.remove(isActiveClass);
featuredList.querySelector("li.is-active").classList.remove(isActiveClass);
let index = Array.from(thumbItems).indexOf(el);
el.classList.add(isActiveClass);
featuredList
.querySelector(`li:nth-child(${++index})`)
.classList.add(isActiveClass);
});
});
document.addEventListener("keyup", (e) => {
if (e.keyCode === 38 || e.keyCode === 40) {
const activeThumb = thumbList.querySelector("li.is-active");
// up arrow
if (e.keyCode === 38) {
if (activeThumb.previousElementSibling) {
activeThumb.previousElementSibling.click();
} else {
thumbList.lastElementChild.click();
}
} else {
// down arrow
if (activeThumb.nextElementSibling) {
activeThumb.nextElementSibling.click();
} else {
thumbList.firstElementChild.click();
}
}
}
});
Draggable.create(featuredImgs, {
bounds: featuredList,
inertia: true
});
/* LIGHTBOX GALLERY
–––––––––––––––––––––––––––––––––––––––––––––––––– */
openLightbox.addEventListener("click", () => {
if (lightboxItems.querySelector("li.is-active")) {
lightboxItems.querySelector("li.is-active").classList.remove(isActiveClass);
}
body.classList.add(overflowYHiddenClass);
const el = featuredList.querySelector("li.is-active");
let index = Array.from(featuredItems).indexOf(el);
lightboxItems
.querySelector(`li:nth-child(n+${++index})`)
.classList.add(isActiveClass);
lightbox.classList.add(isVisibleClass);
});
document.addEventListener("click", (e) => {
if (e.target === closeLightbox) {
body.classList.remove(overflowYHiddenClass);
lightbox.classList.remove(isVisibleClass);
}
});
document.addEventListener("keyup", (e) => {
// Esc
if (document.querySelector(".lightbox.is-visible") && e.keyCode === 27) {
body.classList.remove(overflowYHiddenClass);
lightbox.classList.remove(isVisibleClass);
}
});
for (const lightboxControl of lightboxControls) {
lightboxControl.addEventListener("click", (e) => {
const activeSlide = lightboxItems.querySelector("li.is-active");
activeSlide.classList.remove(isActiveClass);
if (e.currentTarget === lightboxNextControl) {
activeSlide.nextElementSibling
? activeSlide.nextElementSibling.classList.add(isActiveClass)
: lightboxItems.firstElementChild.classList.add(isActiveClass);
} else {
activeSlide.previousElementSibling
? activeSlide.previousElementSibling.classList.add(isActiveClass)
: lightboxItems.lastElementChild.classList.add(isActiveClass);
}
});
}
document.addEventListener("keyup", (e) => {
if (
document.querySelector(".lightbox.is-visible") &&
(e.keyCode === 37 || e.keyCode === 39)
) {
// left arrow
if (e.keyCode === 37) {
lightboxPrevControl.click();
} else {
// next arrow
lightboxNextControl.click();
}
}
});
This Pen doesn't use any external CSS resources.