feat(dailies): 4:3 photo strip, lightbox, and dot sync

- Aspect ratio 3:2 → 4:3 (less aggressive crop; closer to phone native)
- Slides become <button> elements with data-full pointing to original image
- Tap/click any photo in the feed opens a full-screen lightbox showing the
  uncropped original; prev/next browses all feed photos; Esc/arrows/backdrop
  click close
- Dot indicator now syncs with scroll via IntersectionObserver

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:06:54 +02:00
parent 604ba00c70
commit 770a96b099
2 changed files with 82 additions and 4 deletions
+9 -1
View File
@@ -216,8 +216,13 @@ body::after {
.journal-photo-slide {
flex: 0 0 100%;
scroll-snap-align: start;
aspect-ratio: 3 / 2;
aspect-ratio: 4 / 3;
overflow: hidden;
cursor: zoom-in;
background: none;
border: none;
padding: 0;
display: block;
}
.journal-photo-slide img {
@@ -225,8 +230,11 @@ body::after {
height: 100%;
object-fit: cover;
display: block;
transition: opacity 0.15s;
}
.journal-photo-slide:hover img { opacity: 0.9; }
.journal-photo-dots {
display: flex;
justify-content: center;
+73 -3
View File
@@ -123,9 +123,9 @@ feedMap.on('load', function () {
{% if images|length > 0 %}
<div class="journal-photo-strip" data-slides="{{ images|length }}">
{% for img in images %}
<div class="journal-photo-slide">
<img src="{{ img.cropResize(900, 600).url }}" alt="{{ entry.title }}" loading="lazy">
</div>
<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 %}
</div>
{% if images|length > 1 %}
@@ -163,4 +163,74 @@ feedMap.on('load', function () {
<p class="feed-empty">No entries yet. The journey is about to begin.</p>
{% 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>
/* ── Journal photo strip: dot sync + lightbox ───────────────── */
(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');
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);
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 %}