<p class="notification">Click an image to reveal the lightbox</p>
<h1 class="text-center mb-4">Meet Ireland through photos</h1>
<section class="image-grid">
  <div class="container-xxl">
    <div class="row gy-4">
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland1.jpg" class="img-fluid" alt="Ring of Kerry, County Kerry, Ireland" data-caption="Ring of Kerry, County Kerry, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland2.jpg" class="img-fluid" alt="Fintown, Ireland" data-caption="Fintown, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland3.jpg" class="img-fluid" alt="Anne Street, Dublin, Ireland" data-caption="Anne Street, Dublin, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland4.jpg" class="img-fluid" alt="Doonagore Castle, Doolin, Ireland" data-caption="Doonagore Castle, Doolin, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland5.jpg" class="img-fluid" alt="Connemara National Park, Letterfrack, Ireland" data-caption="Connemara National Park, Letterfrack, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland6.jpg" class="img-fluid" alt="Galway, Ireland" data-caption="Galway, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland7.jpg" class="img-fluid" alt="Connemara National Park, Letterfrack, Ireland" data-caption="Connemara National Park, Letterfrack, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland8.jpg" class="img-fluid" alt="The Forty Foot, Dublin 18, Ireland" data-caption="The Forty Foot, Dublin 18, Ireland">
          </a>
        </figure>
      </div>
      <div class="col-12 col-sm-6 col-md-4">
        <figure>
          <a class="d-block" href="">
            <img width="1920" height="1280" src="https://assets.codepen.io/162656/ireland9.jpg" class="img-fluid" alt="Coliemore Harbour, Dublin, Ireland" data-caption="Coliemore Harbour, Dublin, Ireland">
          </a>
        </figure>
      </div>
    </div>
  </div>
</section>

<div class="modal lightbox-modal" id="lightbox-modal" tabindex="-1">
  <div class="modal-dialog modal-fullscreen">
    <div class="modal-content">
      <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
      <div class="modal-body">
        <div class="container-fluid p-0">
          <!-- JS content here -->
        </div>
      </div>
    </div>
  </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>
/* BASIC STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
:root {
  --yellow: #fffbbc;
  --lightbox: #242424;
}

body {
  margin: 24px 0 48px;
  font: 20px / 28px "Marck Script", cursive;
}

.notification {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 5px 15px;
  margin: 0;
  text-align: center;
  z-index: 1;
  background: var(--yellow);
}

@media (max-width: 700px) {
  .notification {
    display: none;
  }
}


/* IMAGE GRID STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.image-grid figure {
  margin-bottom: 0;
}

.image-grid img {
  box-shadow: 0 1rem 1rem rgba(0, 0, 0, 0.15);
  transition: box-shadow 0.2s;
}

.image-grid a:hover img {
  box-shadow: 0 1rem 1rem rgba(0, 0, 0, 0.35);
}


/* LIGHTBOX STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.lightbox-modal .modal-content {
  background: var(--lightbox);
}

.lightbox-modal .btn-close {
  position: absolute;
  top: 20px;
  right: 18px;
  font-size: 1.2rem;
  z-index: 10;
}

.lightbox-modal .modal-body {
  display: flex;
  align-items: center;
  padding: 0;
  text-align: center;
}

.lightbox-modal img {
  width: auto;
  max-height: 100vh;
  max-width: 100%;
}

.lightbox-modal .carousel-caption {
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(36, 36, 36, 0.75);
}

.lightbox-modal .carousel-control-prev,
.lightbox-modal .carousel-control-next {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
  width: auto;
}

.lightbox-modal .carousel-control-prev {
  left: 10px;
}

.lightbox-modal .carousel-control-next {
  right: 10px;
}


/* FOOTER STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.page-footer {
  position: fixed;
  right: 0;
  bottom: 60px;
  display: flex;
  align-items: center;
  font-size: 1rem;
  padding: 5px;
  background: rgba(255, 255, 255, 0.65);
}

.page-footer a {
  display: flex;
  margin-left: 9px;
}
const imageGrid = document.querySelector(".image-grid");
const links = imageGrid.querySelectorAll("a");
const imgs = imageGrid.querySelectorAll("img");
const lightboxModal = document.getElementById("lightbox-modal");
const bsModal = new bootstrap.Modal(lightboxModal);
const modalBody = document.querySelector(".modal-body .container-fluid");

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.parentElement.parentElement;
      const index = [...parentCol.parentElement.children].indexOf(parentCol);
      const bsCarousel = new bootstrap.Carousel(lightboxCarousel);
      bsCarousel.to(index);
    } else {
      createCarousel(currentImg);
    }
    bsModal.show();
  });
}

function createCarousel(img) {
  const markup = `
    <div id="lightboxCarousel" class="carousel slide carousel-fade" data-bs-ride="carousel" data-bs-interval="false">
      <div class="carousel-inner">
        ${createSlides(img)}
      </div> 
      <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;
}

function createSlides(img) {
  let markup = "";
  const currentImgSrc = img.getAttribute("src");

  for (const img of imgs) {
    const imgSrc = img.getAttribute("src");
    const imgAlt = img.getAttribute("alt");
    const imgCaption = img.getAttribute("data-caption");

    markup += `
    <div class="carousel-item${currentImgSrc === imgSrc ? " active" : ""}">
      <img src=${imgSrc} alt=${imgAlt}>
      ${imgCaption ? createCaption(imgCaption) : ""}
    </div>
    `;
  }

  return markup;
}

function createCaption(caption) {
  return `<div class="carousel-caption">
     <p class="m-0">${caption}</p>
    </div>`;
}

External CSS

  1. https://fonts.googleapis.com/css2?family=Marck+Script&amp;display=swap
  2. https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css

External JavaScript

  1. https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js