the home site for me: also iteration 3 or 4 of my site

feat: add a lightbox implementation

dunkirk.sh 83a8f6cf 8b44fc5b

verified
Changed files
+215 -8
sass
static
templates
+109
sass/css/_lightbox.scss
···
+
#lightbox {
+
display: none;
+
position: fixed;
+
top: 0;
+
left: 0;
+
width: 100%;
+
height: 100%;
+
background-color: rgba(0, 0, 0, 0.8);
+
z-index: 9999;
+
justify-content: center;
+
align-items: center;
+
}
+
+
.lightbox-content {
+
position: relative;
+
max-width: 90%;
+
max-height: 90%;
+
display: flex;
+
flex-direction: column;
+
align-items: center;
+
justify-content: center;
+
}
+
+
#lightbox-img {
+
max-width: 100%;
+
max-height: 80vh;
+
object-fit: contain;
+
border: none;
+
padding: 0;
+
margin: 0;
+
background: transparent;
+
border-radius: 0;
+
}
+
+
.lightbox-controls {
+
display: flex;
+
gap: 2rem;
+
margin-top: 1rem;
+
align-items: center;
+
}
+
+
.lightbox-close {
+
position: fixed;
+
top: 20px;
+
right: 20px;
+
font-size: 40px;
+
color: var(--text);
+
background: transparent !important;
+
border: none;
+
cursor: pointer;
+
padding: 0;
+
line-height: 1;
+
-webkit-tap-highlight-color: transparent;
+
transition: color 120ms ease, transform 300ms ease;
+
}
+
+
.lightbox-close:hover {
+
background: transparent !important;
+
color: var(--accent);
+
background-color: transparent !important;
+
transform: rotate(90deg);
+
}
+
+
.lightbox-close:focus {
+
background: transparent !important;
+
background-color: transparent !important;
+
}
+
+
.lightbox-prev,
+
.lightbox-next {
+
font-size: 30px;
+
color: var(--text);
+
background: transparent !important;
+
border: none;
+
cursor: pointer;
+
padding: 0.5rem 1rem;
+
user-select: none;
+
-webkit-tap-highlight-color: transparent;
+
transition: color 120ms ease;
+
}
+
+
.lightbox-prev:hover,
+
.lightbox-next:hover {
+
background: transparent !important;
+
color: var(--accent);
+
background-color: transparent !important;
+
}
+
+
.lightbox-prev:focus,
+
.lightbox-next:focus {
+
background: transparent !important;
+
background-color: transparent !important;
+
}
+
+
@media only screen and (max-width: 720px) {
+
.lightbox-close {
+
top: 10px;
+
right: 10px;
+
}
+
}
+
+
.img-container {
+
cursor: pointer;
+
transition: opacity 120ms ease;
+
}
+
+
.img-container:hover {
+
opacity: 0.9;
+
}
+1
sass/css/main.scss
···
@use "copy-button";
@use "view-transitions";
@use "emoji-inline";
+
@use "lightbox";
+3 -5
sass/css/mods.css
···
}
.center .img-container {
-
display: block;
margin: 1rem auto;
}
···
gap: 1rem;
max-width: 100%;
justify-content: center;
+
align-items: flex-start;
}
.img-group .img-container {
-
flex: 1;
-
min-width: 0;
background-color: var(--accent);
border-bottom: 4px solid var(--bg-light);
border-radius: 7px 7px 10px 10px;
padding: 0.35rem;
margin: 1rem 0;
+
line-height: 0;
}
.img-group img {
-
width: 100%;
+
max-width: 100%;
height: auto;
-
object-fit: contain;
border-radius: 0.35rem;
}
+92
static/lightbox.js
···
+
let currentLightboxImages = [];
+
let currentLightboxIndex = 0;
+
+
function openLightbox(src) {
+
currentLightboxImages = [src];
+
currentLightboxIndex = 0;
+
showLightbox();
+
}
+
+
function openLightboxGroup(element) {
+
const group = element.closest('.img-group');
+
const images = Array.from(group.querySelectorAll('img')).map(img => img.src);
+
const clickedImg = element.querySelector('img');
+
+
currentLightboxImages = images;
+
currentLightboxIndex = images.indexOf(clickedImg.src);
+
showLightbox();
+
}
+
+
function showLightbox() {
+
let lightbox = document.getElementById('lightbox');
+
+
if (!lightbox) {
+
lightbox = document.createElement('div');
+
lightbox.id = 'lightbox';
+
lightbox.innerHTML = `
+
<div class="lightbox-content">
+
<button class="lightbox-close" onclick="closeLightbox()">&times;</button>
+
<img id="lightbox-img" src="" alt="">
+
<div class="lightbox-controls">
+
<button class="lightbox-prev" onclick="prevImage()">←</button>
+
<button class="lightbox-next" onclick="nextImage()">→</button>
+
</div>
+
</div>
+
`;
+
document.body.appendChild(lightbox);
+
+
lightbox.addEventListener('click', (e) => {
+
if (e.target === lightbox) closeLightbox();
+
});
+
+
document.addEventListener('keydown', handleKeyPress);
+
}
+
+
updateLightboxImage();
+
lightbox.style.display = 'flex';
+
document.body.style.overflow = 'hidden';
+
}
+
+
function closeLightbox() {
+
const lightbox = document.getElementById('lightbox');
+
if (lightbox) {
+
lightbox.style.display = 'none';
+
document.body.style.overflow = '';
+
}
+
}
+
+
function updateLightboxImage() {
+
const img = document.getElementById('lightbox-img');
+
const controls = document.querySelector('.lightbox-controls');
+
+
img.src = currentLightboxImages[currentLightboxIndex];
+
+
if (currentLightboxImages.length === 1) {
+
controls.style.display = 'none';
+
} else {
+
controls.style.display = 'flex';
+
}
+
}
+
+
function prevImage() {
+
currentLightboxIndex = (currentLightboxIndex - 1 + currentLightboxImages.length) % currentLightboxImages.length;
+
updateLightboxImage();
+
}
+
+
function nextImage() {
+
currentLightboxIndex = (currentLightboxIndex + 1) % currentLightboxImages.length;
+
updateLightboxImage();
+
}
+
+
function handleKeyPress(e) {
+
const lightbox = document.getElementById('lightbox');
+
if (!lightbox || lightbox.style.display !== 'flex') return;
+
+
if (e.key === 'Escape') {
+
closeLightbox();
+
} else if (e.key === 'ArrowLeft') {
+
prevImage();
+
} else if (e.key === 'ArrowRight') {
+
nextImage();
+
}
+
}
+7
templates/head.html
···
defer
></script>
+
{% set lightboxJsHash = get_hash(path="lightbox.js", sha_type=256,
+
base64=true) %}
+
<script
+
src="{{ get_url(path='lightbox.js?' ~ lightboxJsHash, trailing_slash=false) | safe }}"
+
defer
+
></script>
+
<script type="speculationrules">
{
"prerender": [
+1 -1
templates/shortcodes/img.html
···
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
-
<div class="img-container">
+
<div class="img-container" onclick="openLightbox('{{id}}')">
<img src="{{id}}" {% if alt %}alt="{{alt}}" {% endif %} />
</div>
{% if caption %}
+2 -2
templates/shortcodes/imgs.html
···
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
-
<div class="img-group">
+
<div class="img-group" data-images="{{id}}" data-alts="{{alt | default(value='')}}">
{% set images = id | split(pat=",") %}
{% set alts = alt | default(value="") | split(pat=",") %}
{% for image in images %}
-
<div class="img-container">
+
<div class="img-container" onclick="openLightboxGroup(this)">
<img src="{{image | trim}}" {% if alts[loop.index0] %}alt="{{alts[loop.index0] | trim}}" {% endif %} />
</div>
{% endfor %}