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:
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user