<light-box>
  <ul>
    <li>
      <a href="https://images.unsplash.com/photo-1493548578639-b0c241186eb0?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wyMDcwNnwwfDF8c2VhcmNofDJ8fGtpdHRlbnxlbnwwfHx8fDE3MjM1ODExMjR8MA&ixlib=rb-4.0.3&q=80&w=1920"><img src='https://images.unsplash.com/photo-1493548578639-b0c241186eb0?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wyMDcwNnwwfDF8c2VhcmNofDJ8fGtpdHRlbnxlbnwwfHx8fDE3MjM1ODExMjR8MA&ixlib=rb-4.0.3&q=80&w=400' alt='a cat asleep'></a>
    </li>
    <li>
      <a href="https://images.unsplash.com/photo-1498336179775-9836baef8fdf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wyMDcwNnwwfDF8c2VhcmNofDF8fGtpdHRlbnxlbnwwfHx8fDE3MjM1ODExMjR8MA&ixlib=rb-4.0.3&q=80&w=1920"><img src="https://images.unsplash.com/photo-1498336179775-9836baef8fdf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wyMDcwNnwwfDF8c2VhcmNofDF8fGtpdHRlbnxlbnwwfHx8fDE3MjM1ODExMjR8MA&ixlib=rb-4.0.3&q=80&w=400" alt="a kitten asleep on its back"></a>
    </li>
    <li>
      <a href="https://images.unsplash.com/photo-1445499348736-29b6cdfc03b9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wyMDcwNnwwfDF8c2VhcmNofDR8fGtpdHRlbnxlbnwwfHx8fDE3MjM1ODExMjR8MA&ixlib=rb-4.0.3&q=80&w=1920"><img src="https://images.unsplash.com/photo-1445499348736-29b6cdfc03b9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wyMDcwNnwwfDF8c2VhcmNofDR8fGtpdHRlbnxlbnwwfHx8fDE3MjM1ODExMjR8MA&ixlib=rb-4.0.3&q=80&w=400" alt="a kitten looking past the camera"></a>
    </li>
  </ul>
</light-box>
ul {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

img {
  width: 100%;
}
class LightBox extends HTMLElement {
  #lightBoxes;

  constructor() {
    super();
    this.#lightBoxes = new Map();
    const style = document.createElement("style");
    style.textContent = `
    :where(light-box>dialog>img){
      max-width: 100%;
    }
    :where(light-box>dialog>form){
      display:flex;
      place-content: center;
    }
    :where(light-box>dialog:has(img):first-of-type button[value="prev"]){
      display:none;
    }
    :where(light-box>dialog:has(img):last-of-type button[value="next"]){
      display:none;
    }
    :where(light-box>dialog)::backdrop{
      background-color: rgba(0,0,0,0.25);
      -webkit-backdrop-filter: blur(0.25rem);
      backdrop-filter: blur(0.25rem);
    }`;
    this.append(style);

    // Define this here for correct `this` context.
    this.handleOpenLightBox = (e) => {
      e.preventDefault();
      const dialog = this.#lightBoxes.get(e.target.closest("a"));
      dialog.showModal();
    };
    this.handleCloseLightBox = (e) => {
      console.log(e);
      const dialog = e.target;
      const value = dialog.returnValue;
      let el;
      if (value === "next") {
        el = dialog.nextElementSibling;
      } else if (value === "prev") {
        el = dialog.previousElementSibling;
      }
      if (el instanceof HTMLElement && el.tagName === "DIALOG") {
        el.showModal();
      }
    };
  }

  createLightBox(el) {
    const thumbImage = el.querySelector("img");
    const dialog = document.createElement("dialog");
    dialog.innerHTML = `
      <img src="${el.href}" alt="${thumbImage.alt}" >
      <form method="dialog">
        <button value="close">Close</button>
      </form>
      <form method="dialog">
        <button value="prev">Previous</button>
        <button value="next">Next</button>
      </form>
    `;
    dialog.addEventListener("close", this.handleCloseLightBox);
    this.#lightBoxes.set(el, dialog);
    this.append(dialog);
    el.addEventListener("click", this.handleOpenLightBox);
  }

  doBreakdown() {
    if (this.#lightBoxes.size) {
      this.#lightBoxes.entries()?.forEach(([el, dialog]) => {
        dialog.removeEventListener("close", this.handleCloseLightBox);
        this.removeChild(dialog);
        el.removeEventListener("click", this.handleOpenLightBox);
      });
      this.#lightBoxes.clear();
    }
  }

  doStartup() {
    this.doBreakdown();
    this.querySelectorAll("*:not(dialog) a:has(img)").forEach(
      this.createLightBox.bind(this)
    );
  }

  connectedCallback() {
    this.doStartup();
  }
  adoptedCallback() {
    this.doStartup();
  }

  disconnectedCallback() {
    this.doBreakdown();
  }
}

customElements.define("light-box", LightBox);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.