Animated Lightbox
This is useful when you want to display high resolutions images by clicking on their low-resolution thumbnail. I have made this event posters originally.
Note: This version uses CPU for animations, instead of smooth GPU CSS transitions. This version is purposely not the production version. I list all the features below.
Lightbox Gallery 1
In this gallery you will notice the thumbnails blur when clicked on.
Lightbox Gallery 2
In this gallery, you will notice the thumbnails are low resultion. Clicking on one will open it in the lightbox and begin loading the higher resolution image. When the higher resolution image is ready, it is automatically swapped in the lightbox.
Clicking a thumbnail:
- animates image fullscreen from thumbnail location
- darkens background
- blurs the thumbnail
- switches from a zoom-out cursor over the image to a zoom-out cursor over the page
- does not lock scrolling
- preloads a high resolution version of the image and swaps it in the lightbox when fully loaded
- slight blur effect on transition to high res version
- fades in closing × indicator
Clicking the lightbox:
- animates image back into thumbnail location, no matter where the has scrolled
- lightens background
- removes the zoom-out curson to show zoom-in cursors when hovering over image
Source Code
JavaScript
// Updates available
// https://www.albinoblacksheep.com/
// See https://www.albinoblacksheep.com/webmaster/lightbox
// Animated Lightbox by Steven Lerner, Albino Blacksheep
// Copyright 2026
document.querySelectorAll(".gallery img").forEach((thumb) => {
thumb.addEventListener("click", (e) => {
thumb.classList.add("is-active");
const rect = thumb.getBoundingClientRect();
const overlay = document.createElement("div");
overlay.className = "lightbox";
document.body.appendChild(overlay);
const img = document.createElement("img");
img.src = thumb.src;
overlay.appendChild(img);
document.body.classList.add("lightbox-open");
// begind comment out from here if only one SRC
const full = new Image();
full.src = thumb.dataset.full || thumb.src;
full.onload = () => {
if (full.decode) {
full.decode().then(() => {
img.src = full.src;
});
} else {
img.src = full.src;
}
};
// end comment out here if only one SRC
// START: exact thumbnail geometry
img.style.left = rect.left + "px";
img.style.top = rect.top + "px";
img.style.width = rect.width + "px";
img.style.height = rect.height + "px";
requestAnimationFrame(() => {
overlay.classList.add("open");
// END: fullscreen (stable)
img.style.left = "0px";
img.style.top = "0px";
img.style.width = "100vw";
img.style.height = "100vh";
});
function close() {
const rect = thumb.getBoundingClientRect();
overlay.classList.remove("open");
document.body.classList.remove("lightbox-open");
// RETURN: back to current thumbnail position (scroll-safe)
img.style.left = rect.left + "px";
img.style.top = rect.top + "px";
img.style.width = rect.width + "px";
img.style.height = rect.height + "px";
const cleanup = () => {
thumb.classList.remove("is-active");
overlay.remove();
};
overlay.addEventListener("transitionend", cleanup, { once: true });
setTimeout(cleanup, 300);
}
overlay.addEventListener("click", close);
});
});
CSS
/*
https://www.albinoblacksheep.com/
See https://www.albinoblacksheep.com/webmaster/lightbox
Made by Steven Lerner, Albino Blacksheep
Copyright 2026
*/
body:has(.lightbox) {
cursor: zoom-out;
}
.lightbox {
position: fixed;
inset: 0;
background: rgba(0,0,0,0);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
transition:background .4s ease;
pointer-events: all;
}
.lightbox.open {
background: rgba(0,0,0,0.7);
}
.lightbox img {
opacity:1;
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
object-fit: contain;
transition: left 250ms ease, top 250ms ease, width 250ms ease, height 250ms ease;
will-change: left, top, width, height;
transform-origin: 0 0;
}
.gallery img.is-active {
filter: blur(4px);
transform: scale(0.98);
transition: filter 200ms ease, transform 200ms ease;
}
.lightbox::after {
opacity:0;
transition: opacity .2s;
content: "×";
position: fixed;
top: 2em;
right: 1em;
font-size: 2em;
color: white;}
.lightbox.open::after {
opacity: .75;
}
HTML
<div class="gallery">
<img class="thumb" src="image.jpg" width="300" height="300" alt="">
</div>