diff --git a/_includes/layouts/base.njk b/_includes/layouts/base.njk
index f1fd24a..5d47014 100644
--- a/_includes/layouts/base.njk
+++ b/_includes/layouts/base.njk
@@ -72,6 +72,7 @@
{# Alpine.js components — MUST load before Alpine core (Alpine.data() registration via alpine:init) #}
+
diff --git a/_includes/layouts/post.njk b/_includes/layouts/post.njk
index 76ee848..6f8e38c 100644
--- a/_includes/layouts/post.njk
+++ b/_includes/layouts/post.njk
@@ -2,7 +2,7 @@
layout: layouts/base.njk
withBlogSidebar: true
---
-
+
{% if title %}
{{ title }}
{% endif %}
@@ -245,6 +245,23 @@ withBlogSidebar: true
"image": ["{% if postImage.startsWith('http') %}{{ postImage }}{% elif '/' in postImage and postImage[0] == '/' %}{{ site.url }}{{ postImage }}{% else %}{{ site.url }}/{{ postImage }}{% endif %}"]{% endif %}
}
+
+ {# Lightbox overlay for article images #}
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
{# Comments section #}
diff --git a/js/lightbox.js b/js/lightbox.js
new file mode 100644
index 0000000..d8df430
--- /dev/null
+++ b/js/lightbox.js
@@ -0,0 +1,80 @@
+/**
+ * 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();
+ },
+ }));
+});