From 1998820decf6e81abc5f196a1ca03fbddacd8ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Sat, 11 Apr 2026 18:24:33 +0200 Subject: [PATCH] feat: gallery lightbox with Alpine.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hugo pre-computes a JSON array of {img, video} entries per gallery item (WebP 1200w src + optional mp4 src). Alpine reads the array and manages lightbox state (open/idx/prev/next/close). Features: - Click any card → opens fullscreen dark overlay (bg-void/95 + backdrop-blur) - Videos: controls, autoplay, x-effect reloads src on navigate - Images: full-res WebP centred with object-contain - Keyboard: Escape closes, ←/→ navigate - Dot indicators: active dot expands (heat colour) - Prev/next arrow buttons (hidden when only 1 item) - Grid cards: cursor-zoom-in, pointer-events-none on media so click always hits the
- Teleported to via x-teleport to avoid stacking context issues - Gradient-line accent top and bottom of overlay Co-Authored-By: Claude Sonnet 4.6 --- layouts/posts/single.html | 147 +++++++++++++++++++++++++++++++++----- 1 file changed, 130 insertions(+), 17 deletions(-) diff --git a/layouts/posts/single.html b/layouts/posts/single.html index d3567c3..a38bb83 100644 --- a/layouts/posts/single.html +++ b/layouts/posts/single.html @@ -101,26 +101,139 @@
{{- if $bundleImages -}} -
- Gallery - {{ len $bundleImages }} {{ if eq (len $bundleImages) 1 }}work{{ else }}works{{ end }} -
-
- -
- {{- range $bundleImages -}} + {{- /* Pre-compute lightbox items so Alpine gets a static JSON array */ -}} + {{- $lbItems := slice -}} + {{- range $bundleImages -}} {{- $stem := .Name | strings.TrimSuffix ".png" -}} {{- $video := $.Resources.GetMatch (printf "%s.mp4" $stem) -}} -
-
- {{- if $video -}} - - {{- else -}} - {{- partial "img.html" (dict "res" . "widths" (slice 800 1200) "sizes" "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" "class" "w-full") -}} - {{- end -}} + {{- $webp := .Resize "1200x webp" -}} + {{- $entry := dict "img" $webp.RelPermalink "video" "" -}} + {{- if $video -}}{{- $entry = dict "img" $webp.RelPermalink "video" $video.RelPermalink -}}{{- end -}} + {{- $lbItems = $lbItems | append $entry -}} + {{- end -}} + +
+ +
+ Gallery + {{ len $bundleImages }} {{ if eq (len $bundleImages) 1 }}work{{ else }}works{{ end }} +
+
+ + +
+ {{- range $i, $img := $bundleImages -}} + {{- $stem := $img.Name | strings.TrimSuffix ".png" -}} + {{- $video := $.Resources.GetMatch (printf "%s.mp4" $stem) -}} +
+
+ {{- if $video -}} + + {{- else -}} + {{- partial "img.html" (dict "res" $img "widths" (slice 800 1200) "sizes" "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" "class" "w-full pointer-events-none") -}} + {{- end -}} +
+
+ {{- end -}} +
+ + + +
{{- else -}}