feat(dailies): replace custom lightbox with PhotoSwipe v5

Drops ~80 lines of fragile custom lightbox JS/HTML/CSS in favour of
PhotoSwipe v5 loaded from CDN. Slides are now <a> tags (the pswp-gallery
children) which always fire click events on iOS even inside scroll
containers — the root cause of the mobile tap issue. Pinch-to-zoom and
swipe-to-dismiss come for free. Dot sync kept as a separate vanilla JS
block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
This commit is contained in:
2026-06-21 19:31:27 +02:00
parent 770a96b099
commit 30c8937566
2 changed files with 39 additions and 114 deletions
+29 -60
View File
@@ -121,12 +121,19 @@ feedMap.on('load', function () {
{% set images = entry.media.images %}
{% if images|length > 0 %}
<div class="journal-photo-strip" data-slides="{{ images|length }}">
{% for img in images %}
<button class="journal-photo-slide" data-full="{{ img.url }}" data-alt="{{ entry.title }}" aria-label="View photo">
<img src="{{ img.cropResize(900, 675).url }}" alt="{{ entry.title }}" loading="lazy">
</button>
{% endfor %}
{% do assets.addCss('https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css') %}
<div class="journal-photo-wrap">
<div class="journal-photo-strip pswp-gallery" id="gallery-{{ entry.slug }}">
{% for img in images %}
<a class="journal-photo-slide"
href="{{ img.url }}"
data-pswp-width="{{ img.width }}"
data-pswp-height="{{ img.height }}"
target="_blank">
<img src="{{ img.cropResize(900, 675).url }}" alt="{{ entry.title }}" loading="lazy">
</a>
{% endfor %}
</div>
</div>
{% if images|length > 1 %}
<div class="journal-photo-dots" aria-hidden="true">
@@ -164,73 +171,35 @@ feedMap.on('load', function () {
{% endif %}
</div>
<div class="lightbox" id="feed-lightbox" role="dialog" aria-modal="true" aria-label="Photo viewer" hidden>
<button class="lightbox-close" id="feed-lb-close" aria-label="Close">✕</button>
<button class="lightbox-prev" id="feed-lb-prev" aria-label="Previous"></button>
<img class="lightbox-img" id="feed-lb-img" src="" alt="">
<button class="lightbox-next" id="feed-lb-next" aria-label="Next"></button>
</div>
<script type="module">
import PhotoSwipeLightbox from 'https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe-lightbox.esm.min.js';
const lightbox = new PhotoSwipeLightbox({
gallery: '.pswp-gallery',
children: 'a.journal-photo-slide',
pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.esm.min.js')
});
lightbox.init();
</script>
<script>
/* ── Journal photo strip: dot sync + lightbox ───────────────── */
/* ── Dot sync ────────────────────────────────────────────────── */
(function () {
/* Dot sync */
document.querySelectorAll('.journal-photo-strip').forEach(function (strip) {
var slides = strip.querySelectorAll('.journal-photo-slide');
var strip_id = strip.closest('article');
if (!strip_id) return;
var dots = strip_id.querySelectorAll('.journal-photo-dot');
document.querySelectorAll('.journal-photo-wrap').forEach(function (wrap) {
var strip = wrap.querySelector('.journal-photo-strip');
var slides = Array.from(strip.querySelectorAll('.journal-photo-slide'));
var article = wrap.closest('article');
var dots = article ? Array.from(article.querySelectorAll('.journal-photo-dot')) : [];
if (!dots.length) return;
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
var idx = Array.from(slides).indexOf(e.target);
var idx = slides.indexOf(e.target);
dots.forEach(function (d) { d.classList.remove('is-active'); });
if (dots[idx]) dots[idx].classList.add('is-active');
});
}, { root: strip, threshold: 0.5 });
slides.forEach(function (s) { io.observe(s); });
});
/* Lightbox */
var allSlides = Array.from(document.querySelectorAll('.journal-photo-slide[data-full]'));
if (!allSlides.length) return;
var lightbox = document.getElementById('feed-lightbox');
var lbImg = document.getElementById('feed-lb-img');
var current = 0;
function open(index) {
current = index;
lbImg.src = allSlides[index].dataset.full;
lbImg.alt = allSlides[index].dataset.alt;
lightbox.hidden = false;
document.body.style.overflow = 'hidden';
document.getElementById('feed-lb-close').focus();
}
function close() {
lightbox.hidden = true;
document.body.style.overflow = '';
allSlides[current].focus();
}
function prev() { open((current - 1 + allSlides.length) % allSlides.length); }
function next() { open((current + 1) % allSlides.length); }
allSlides.forEach(function (slide, i) {
slide.addEventListener('click', function () { open(i); });
});
document.getElementById('feed-lb-close').addEventListener('click', close);
document.getElementById('feed-lb-prev').addEventListener('click', prev);
document.getElementById('feed-lb-next').addEventListener('click', next);
lightbox.addEventListener('click', function (e) { if (e.target === lightbox) close(); });
document.addEventListener('keydown', function (e) {
if (lightbox.hidden) return;
if (e.key === 'Escape') close();
if (e.key === 'ArrowLeft') prev();
if (e.key === 'ArrowRight') next();
});
})();
</script>
{% endblock %}