Responsive Image Gallery with Lightbox Feature
Create a responsive image gallery with a lightbox to enhance user viewing experience.
Code
<!--
Responsive Image Gallery + Lightbox (accessible, responsive, production-ready).
Usage: Replace image src/alt, add more .gallery-img elements; no dependencies required.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Responsive Image Gallery with Lightbox</title>
<style>
:root { color-scheme: light dark; --gap: 12px; }
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--gap); padding: 16px; max-width: 1100px; margin: 0 auto; }
.gallery-img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 12px; cursor: zoom-in; transition: transform .15s ease; background: #111; }
.gallery-img:hover { transform: translateY(-2px); }
.lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.86); z-index: 9999; padding: 24px; }
.lightbox[aria-hidden="false"] { display: grid; place-items: center; }
.lightbox-content { max-width: min(92vw, 1100px); max-height: 78vh; width: auto; height: auto; border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,.5); }
#caption { margin-top: 12px; color: #f2f2f2; text-align: center; max-width: min(92vw, 1100px); line-height: 1.35; }
.close { position: fixed; top: 14px; right: 18px; width: 44px; height: 44px; border: 0; border-radius: 999px; background: rgba(255,255,255,.12); color: #fff; font-size: 28px; cursor: pointer; }
.close:focus-visible { outline: 3px solid #7aa7ff; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .gallery-img { transition: none; } }
</style>
</head>
<body>
<!-- HTML structure for the gallery -->
<div class="gallery" aria-label="Image gallery">
<img src="image1.jpg" alt="Image 1" class="gallery-img" loading="lazy" decoding="async">
<img src="image2.jpg" alt="Image 2" class="gallery-img" loading="lazy" decoding="async">
<img src="image3.jpg" alt="Image 3" class="gallery-img" loading="lazy" decoding="async">
</div>
<!-- Lightbox element -->
<div id="lightbox" class="lightbox" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Image preview">
<button type="button" class="close" aria-label="Close preview">×</button>
<img class="lightbox-content" id="lightbox-img" alt="">
<div id="caption" aria-live="polite"></div>
</div>
<script>
/* Modular lightbox: event delegation for performance + focus/ESC/outside-click handling. */
(() => {
const gallery = document.querySelector('.gallery');
const lightbox = document.getElementById('lightbox');
const lightboxImg = document.getElementById('lightbox-img');
const caption = document.getElementById('caption');
const closeButton = lightbox.querySelector('.close');
if (!gallery || !lightbox || !lightboxImg || !caption || !closeButton) return;
let lastActiveEl = null;
const openLightbox = (img) => {
if (!img?.src) return;
lastActiveEl = document.activeElement;
lightboxImg.src = img.currentSrc || img.src; // currentSrc supports srcset for crisp previews
lightboxImg.alt = img.alt || '';
caption.textContent = img.alt || '';
lightbox.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden'; // prevent background scroll
closeButton.focus();
};
const closeLightbox = () => {
lightbox.setAttribute('aria-hidden', 'true');
lightboxImg.removeAttribute('src'); // release memory for large images
document.body.style.overflow = '';
if (lastActiveEl && typeof lastActiveEl.focus === 'function') lastActiveEl.focus();
};
gallery.addEventListener('click', (e) => {
const img = e.target.closest('.gallery-img');
if (img) openLightbox(img);
});
closeButton.addEventListener('click', closeLightbox);
lightbox.addEventListener('click', (e) => {
if (e.target === lightbox) closeLightbox(); // click backdrop to close
});
document.addEventListener('keydown', (e) => {
if (lightbox.getAttribute('aria-hidden') === 'false' && e.key === 'Escape') closeLightbox();
});
})();
// Example usage: Add more <img class="gallery-img" src="..." alt="..."> inside .gallery.
</script>
</body>
</html>