Files
blog-eleventy-indiekit/js/lightbox.js
Ricardo e2c40468b6 feat: add fullscreen lightbox for article images
Alpine.js component that lets visitors click any image inside
article content to view it fullscreen with keyboard navigation
(arrow keys, Escape to close) and prev/next buttons.
2026-02-27 10:14:35 +01:00

81 lines
2.1 KiB
JavaScript

/**
* Alpine.js lightbox component for article images.
* Registers via alpine:init so it's available before Alpine starts.
* Click any image inside .e-content to view fullscreen.
* Navigate with arrow keys, close with Escape or click outside.
*/
document.addEventListener("alpine:init", () => {
Alpine.data("lightbox", () => ({
open: false,
src: "",
alt: "",
images: [],
currentIndex: 0,
init() {
const container = this.$root;
const imgs = container.querySelectorAll(
".e-content img:not(.u-photo)"
);
this.images = Array.from(imgs);
this.images.forEach((img, i) => {
img.style.cursor = "zoom-in";
img.addEventListener("click", (e) => {
e.preventDefault();
this.show(i);
});
});
},
show(index) {
this.currentIndex = index;
const img = this.images[index];
// Use the largest source available
const picture = img.closest("picture");
if (picture) {
const source = picture.querySelector("source");
if (source) {
// Extract the URL from srcset (strip width descriptor)
const srcset = source.getAttribute("srcset") || "";
this.src = srcset.split(/\s+/)[0] || img.src;
} else {
this.src = img.src;
}
} else {
this.src = img.src;
}
this.alt = img.alt || "";
this.open = true;
document.body.style.overflow = "hidden";
},
close() {
this.open = false;
this.src = "";
document.body.style.overflow = "";
},
next() {
if (this.images.length > 1) {
this.show((this.currentIndex + 1) % this.images.length);
}
},
prev() {
if (this.images.length > 1) {
this.show(
(this.currentIndex - 1 + this.images.length) % this.images.length
);
}
},
onKeydown(e) {
if (!this.open) return;
if (e.key === "Escape") this.close();
if (e.key === "ArrowRight") this.next();
if (e.key === "ArrowLeft") this.prev();
},
}));
});