Lightbox for Bootstrap 5.3.x, based on Modal and Carousel component. Responsive content, auto-play after first interaction, dark mode enabled, auto zoom. Fullscreen Mode enabled.
Created
May 15, 2025 15:42
-
-
Save anoduck/b60af6a4d771b951ed1953693a4f65dd to your computer and use it in GitHub Desktop.
Bootstrap 5 Lightbox Gallery
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<svg class="d-none" xmlns="http://www.w3.org/2000/svg"> | |
<symbol id="enlarge" viewBox="0 0 16 16"> | |
<path d="M1.5 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4A1.5 1.5 0 0 1 1.5 0h4a.5.5 0 0 1 0 1h-4zM10 .5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 16 1.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5zM.5 10a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 0 14.5v-4a.5.5 0 0 1 .5-.5zm15 0a.5.5 0 0 1 .5.5v4a1.5 1.5 0 0 1-1.5 1.5h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5z"/> | |
</symbol> | |
<symbol id="exit" viewBox="0 0 16 16"> | |
<path d="M5.5 0a.5.5 0 0 1 .5.5v4A1.5 1.5 0 0 1 4.5 6h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5zm5 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 10 4.5v-4a.5.5 0 0 1 .5-.5zM0 10.5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 6 11.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5zm10 1a1.5 1.5 0 0 1 1.5-1.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4z"/> | |
</symbol> | |
</svg> | |
<h1 class="text-center mb-0">Bootstrap 5 Lightbox Gallery</h1> | |
<p class="text-center mb-4">Click an image to reveal the lightbox</p> | |
<section class="photo-gallery"> | |
<div class="container"> | |
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 gallery-grid"> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/251/1200/800.webp"> | |
<img src="https://picsum.photos/id/251/480/320.webp" class="img-fluid" alt="Lorem ipsum dolor sit amet"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/678/1200/800.webp"> | |
<img src="https://picsum.photos/id/678/480/320.webp" class="img-fluid" alt="Ipsum lorem dolor sit amet"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/74/1200/800.webp"> | |
<img src="https://picsum.photos/id/74/480/320.webp" class="img-fluid" alt="Dolor lorem ipsum sit amet"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/92/1200/800.webp"> | |
<img src="https://picsum.photos/id/92/480/320.webp" class="img-fluid" alt="Sit amet lorem ipsum dolor"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/62/1200/800.webp"> | |
<img src="https://picsum.photos/id/62/480/320.webp" class="img-fluid" alt="Aut ipsam deserunt nostrum quo"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/575/1200/800.webp"> | |
<img src="https://picsum.photos/id/575/480/320.webp" class="img-fluid" alt="Nulla ex nihil eligendi tempora"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/110/1200/800.webp"> | |
<img src="https://picsum.photos/id/110/480/320.webp" class="img-fluid" alt="Nemo perspiciatis voluptatum"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/177/1200/800.webp"> | |
<img src="https://picsum.photos/id/177/480/320.webp" class="img-fluid" alt="Accusantium consequuntur modi"> | |
</a> | |
</div> | |
<div class="col"> | |
<a class="gallery-item" href="https://picsum.photos/id/197/1200/800.webp"> | |
<img src="https://picsum.photos/id/197/480/320.webp" class="img-fluid" alt="Dolore asperiores reprehenderit"> | |
</a> | |
</div> | |
</div> | |
</div> | |
</section> | |
<footer id="copyright" class="mt-5 text-center"> | |
<div class="container"> | |
<div class="row"> | |
<div class="col"> | |
<p class="mb-0">With <b>Bootstrap 5.3.3</b> and <b>Vanilla JS</b></p> | |
<p class="mb-0">Photos source: <a target="_blank" href="https://picsum.photos">Picsum photos</a></p> | |
<p class="mb-0">Code made by <a target="_blank" href="https://adorade.ro">Adorade</a>. License <b>MIT</b></p> | |
</div> | |
</div> | |
</div> | |
</footer> | |
<div class="modal fade lightbox-modal" id="lightbox-modal" tabindex="-1"> | |
<div class="modal-dialog modal-dialog-centered modal-fullscreen"> | |
<div class="modal-content"> | |
<button type="button" class="btn-fullscreen-enlarge" aria-label="Enlarge fullscreen"> | |
<svg class="bi"><use href="#enlarge"></use></svg> | |
</button> | |
<button type="button" class="btn-fullscreen-exit d-none" aria-label="Exit fullscreen"> | |
<svg class="bi"><use href="#exit"></use></svg> | |
</button> | |
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |
<div class="modal-body"> | |
<div class="lightbox-content"> | |
<!-- JS content here --> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const html = document.querySelector('html'); | |
html.setAttribute('data-bs-theme', 'dark'); | |
document.addEventListener('DOMContentLoaded', () => { | |
// --- Create LightBox | |
const galleryGrid = document.querySelector(".gallery-grid"); | |
const links = galleryGrid.querySelectorAll("a"); | |
const imgs = galleryGrid.querySelectorAll("img"); | |
const lightboxModal = document.getElementById("lightbox-modal"); | |
const bsModal = new bootstrap.Modal(lightboxModal); | |
const modalBody = lightboxModal.querySelector(".lightbox-content"); | |
function createCaption (caption) { | |
return `<div class="carousel-caption d-none d-md-block"> | |
<h4 class="m-0">${caption}</h4> | |
</div>`; | |
} | |
function createIndicators (img) { | |
let markup = "", i, len; | |
const countSlides = links.length; | |
const parentCol = img.closest('.col'); | |
const curIndex = [...parentCol.parentElement.children].indexOf(parentCol); | |
for (i = 0, len = countSlides; i < len; i++) { | |
markup += ` | |
<button type="button" data-bs-target="#lightboxCarousel" | |
data-bs-slide-to="${i}" | |
${i === curIndex ? 'class="active" aria-current="true"' : ''} | |
aria-label="Slide ${i + 1}"> | |
</button>`; | |
} | |
return markup; | |
} | |
function createSlides (img) { | |
let markup = ""; | |
const currentImgSrc = img.closest('.gallery-item').getAttribute("href"); | |
for (const img of imgs) { | |
const imgSrc = img.closest('.gallery-item').getAttribute("href"); | |
const imgAlt = img.getAttribute("alt"); | |
markup += ` | |
<div class="carousel-item${currentImgSrc === imgSrc ? " active" : ""}"> | |
<img class="d-block img-fluid w-100" src=${imgSrc} alt="${imgAlt}"> | |
${imgAlt ? createCaption(imgAlt) : ""} | |
</div>`; | |
} | |
return markup; | |
} | |
function createCarousel (img) { | |
const markup = ` | |
<!-- Lightbox Carousel --> | |
<div id="lightboxCarousel" class="carousel slide carousel-fade" data-bs-ride="true"> | |
<!-- Indicators/dots --> | |
<div class="carousel-indicators"> | |
${createIndicators(img)} | |
</div> | |
<!-- Wrapper for Slides --> | |
<div class="carousel-inner justify-content-center mx-auto"> | |
${createSlides(img)} | |
</div> | |
<!-- Controls/icons --> | |
<button class="carousel-control-prev" type="button" data-bs-target="#lightboxCarousel" data-bs-slide="prev"> | |
<span class="carousel-control-prev-icon" aria-hidden="true"></span> | |
<span class="visually-hidden">Previous</span> | |
</button> | |
<button class="carousel-control-next" type="button" data-bs-target="#lightboxCarousel" data-bs-slide="next"> | |
<span class="carousel-control-next-icon" aria-hidden="true"></span> | |
<span class="visually-hidden">Next</span> | |
</button> | |
</div> | |
`; | |
modalBody.innerHTML = markup; | |
} | |
for (const link of links) { | |
link.addEventListener("click", function (e) { | |
e.preventDefault(); | |
const currentImg = link.querySelector("img"); | |
const lightboxCarousel = document.getElementById("lightboxCarousel"); | |
if (lightboxCarousel) { | |
const parentCol = link.closest('.col'); | |
const index = [...parentCol.parentElement.children].indexOf(parentCol); | |
const bsCarousel = new bootstrap.Carousel(lightboxCarousel); | |
bsCarousel.to(index); | |
} else { | |
createCarousel(currentImg); | |
} | |
bsModal.show(); | |
}); | |
} | |
// --- Support Fullscreen | |
const fsEnlarge = document.querySelector(".btn-fullscreen-enlarge"); | |
const fsExit = document.querySelector(".btn-fullscreen-exit"); | |
function enterFS () { | |
lightboxModal.requestFullscreen().then({}).catch(err => { | |
alert(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`); | |
}); | |
fsEnlarge.classList.toggle("d-none"); | |
fsExit.classList.toggle("d-none"); | |
} | |
function exitFS () { | |
document.exitFullscreen(); | |
fsExit.classList.toggle("d-none"); | |
fsEnlarge.classList.toggle("d-none"); | |
} | |
fsEnlarge.addEventListener("click", (e) => { | |
e.preventDefault(); | |
enterFS(); | |
}); | |
fsExit.addEventListener("click", (e) => { | |
e.preventDefault(); | |
exitFS(); | |
}); | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
--lightbox: rgb(0 0 0 / 0.75); | |
--carousel-text: #fff; | |
} | |
body { | |
margin: 1.5rem 0 3.5rem; | |
} | |
@keyframes zoomin { | |
0% { | |
transform: scale(1); | |
} | |
50% { | |
transform: scale(1.05); | |
} | |
100% { | |
transform: scale(1); | |
} | |
} | |
.gallery-item { | |
display: block; | |
} | |
.gallery-item img { | |
box-shadow: 0 1rem 1rem rgba(0, 0, 0, 0.15); | |
transition: box-shadow 0.2s; | |
} | |
.gallery-item:hover img { | |
box-shadow: 0 1rem 1rem rgba(0, 0, 0, 0.35); | |
} | |
.lightbox-modal .modal-content { | |
background-color: var(--lightbox); | |
} | |
.lightbox-modal .btn-close { | |
position: absolute; | |
top: 1.25rem; | |
right: 1.25rem; | |
font-size: 1.25rem; | |
z-index: 10; | |
filter: invert(1) grayscale(100); | |
} | |
.lightbox-modal .modal-body { | |
display: flex; | |
align-items: center; | |
padding: 0; | |
} | |
.lightbox-modal .lightbox-content { | |
width: 100%; | |
} | |
.lightbox-modal .carousel-indicators { | |
margin-bottom: 0; | |
} | |
.lightbox-modal .carousel-indicators [data-bs-target] { | |
background-color: var(--carousel-text) !important; | |
} | |
.lightbox-modal .carousel-inner { | |
width: 75%; | |
} | |
.lightbox-modal .carousel-inner img { | |
animation: zoomin 10s linear infinite; | |
} | |
.lightbox-modal .carousel-item .carousel-caption { | |
right: 0; | |
bottom: 0; | |
left: 0; | |
padding-bottom: 2rem; | |
background-color: var(--lightbox); | |
color: var(--carousel-text) !important; | |
} | |
.lightbox-modal .carousel-control-prev, | |
.lightbox-modal .carousel-control-next { | |
width: auto; | |
} | |
.lightbox-modal .carousel-control-prev { | |
left: 1.25rem; | |
} | |
.lightbox-modal .carousel-control-next { | |
right: 1.25rem; | |
} | |
@media (min-width: 1400px) { | |
.lightbox-modal .carousel-inner { | |
max-width: 60%; | |
} | |
} | |
[data-bs-theme = "dark"] .lightbox-modal .carousel-control-next-icon, | |
[data-bs-theme = "dark"] .lightbox-modal .carousel-control-prev-icon { | |
filter: none; | |
} | |
.btn-fullscreen-enlarge, | |
.btn-fullscreen-exit { | |
position: absolute; | |
top: 1.25rem; | |
right: 3.5rem; | |
z-index: 10; | |
border: 0; | |
background: transparent; | |
opacity: .6; | |
font-size: 1.25rem; | |
} | |
.bi { | |
display: inline-block; | |
width: 1em; | |
height: 1em; | |
vertical-align: -0.035em; | |
fill: currentcolor; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment