tangled
alpha
login
or
join now
dunkirk.sh
/
zera
5
fork
atom
the home site for me: also iteration 3 or 4 of my site
5
fork
atom
overview
issues
pulls
pipelines
feat: add a lightbox implementation
dunkirk.sh
3 months ago
83a8f6cf
8b44fc5b
verified
This commit was signed with the committer's
known signature
.
dunkirk.sh
SSH Key Fingerprint:
SHA256:DqcG0RXYExE26KiWo3VxJnsxswN1QNfTBvB+bdSpk80=
+215
-8
7 changed files
expand all
collapse all
unified
split
sass
css
_lightbox.scss
main.scss
mods.css
static
lightbox.js
templates
head.html
shortcodes
img.html
imgs.html
+109
sass/css/_lightbox.scss
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
#lightbox {
2
+
display: none;
3
+
position: fixed;
4
+
top: 0;
5
+
left: 0;
6
+
width: 100%;
7
+
height: 100%;
8
+
background-color: rgba(0, 0, 0, 0.8);
9
+
z-index: 9999;
10
+
justify-content: center;
11
+
align-items: center;
12
+
}
13
+
14
+
.lightbox-content {
15
+
position: relative;
16
+
max-width: 90%;
17
+
max-height: 90%;
18
+
display: flex;
19
+
flex-direction: column;
20
+
align-items: center;
21
+
justify-content: center;
22
+
}
23
+
24
+
#lightbox-img {
25
+
max-width: 100%;
26
+
max-height: 80vh;
27
+
object-fit: contain;
28
+
border: none;
29
+
padding: 0;
30
+
margin: 0;
31
+
background: transparent;
32
+
border-radius: 0;
33
+
}
34
+
35
+
.lightbox-controls {
36
+
display: flex;
37
+
gap: 2rem;
38
+
margin-top: 1rem;
39
+
align-items: center;
40
+
}
41
+
42
+
.lightbox-close {
43
+
position: fixed;
44
+
top: 20px;
45
+
right: 20px;
46
+
font-size: 40px;
47
+
color: var(--text);
48
+
background: transparent !important;
49
+
border: none;
50
+
cursor: pointer;
51
+
padding: 0;
52
+
line-height: 1;
53
+
-webkit-tap-highlight-color: transparent;
54
+
transition: color 120ms ease, transform 300ms ease;
55
+
}
56
+
57
+
.lightbox-close:hover {
58
+
background: transparent !important;
59
+
color: var(--accent);
60
+
background-color: transparent !important;
61
+
transform: rotate(90deg);
62
+
}
63
+
64
+
.lightbox-close:focus {
65
+
background: transparent !important;
66
+
background-color: transparent !important;
67
+
}
68
+
69
+
.lightbox-prev,
70
+
.lightbox-next {
71
+
font-size: 30px;
72
+
color: var(--text);
73
+
background: transparent !important;
74
+
border: none;
75
+
cursor: pointer;
76
+
padding: 0.5rem 1rem;
77
+
user-select: none;
78
+
-webkit-tap-highlight-color: transparent;
79
+
transition: color 120ms ease;
80
+
}
81
+
82
+
.lightbox-prev:hover,
83
+
.lightbox-next:hover {
84
+
background: transparent !important;
85
+
color: var(--accent);
86
+
background-color: transparent !important;
87
+
}
88
+
89
+
.lightbox-prev:focus,
90
+
.lightbox-next:focus {
91
+
background: transparent !important;
92
+
background-color: transparent !important;
93
+
}
94
+
95
+
@media only screen and (max-width: 720px) {
96
+
.lightbox-close {
97
+
top: 10px;
98
+
right: 10px;
99
+
}
100
+
}
101
+
102
+
.img-container {
103
+
cursor: pointer;
104
+
transition: opacity 120ms ease;
105
+
}
106
+
107
+
.img-container:hover {
108
+
opacity: 0.9;
109
+
}
+1
sass/css/main.scss
···
7
@use "copy-button";
8
@use "view-transitions";
9
@use "emoji-inline";
0
···
7
@use "copy-button";
8
@use "view-transitions";
9
@use "emoji-inline";
10
+
@use "lightbox";
+3
-5
sass/css/mods.css
···
110
}
111
112
.center .img-container {
113
-
display: block;
114
margin: 1rem auto;
115
}
116
···
342
gap: 1rem;
343
max-width: 100%;
344
justify-content: center;
0
345
}
346
347
.img-group .img-container {
348
-
flex: 1;
349
-
min-width: 0;
350
background-color: var(--accent);
351
border-bottom: 4px solid var(--bg-light);
352
border-radius: 7px 7px 10px 10px;
353
padding: 0.35rem;
354
margin: 1rem 0;
0
355
}
356
357
.img-group img {
358
-
width: 100%;
359
height: auto;
360
-
object-fit: contain;
361
border-radius: 0.35rem;
362
}
363
···
110
}
111
112
.center .img-container {
0
113
margin: 1rem auto;
114
}
115
···
341
gap: 1rem;
342
max-width: 100%;
343
justify-content: center;
344
+
align-items: flex-start;
345
}
346
347
.img-group .img-container {
0
0
348
background-color: var(--accent);
349
border-bottom: 4px solid var(--bg-light);
350
border-radius: 7px 7px 10px 10px;
351
padding: 0.35rem;
352
margin: 1rem 0;
353
+
line-height: 0;
354
}
355
356
.img-group img {
357
+
max-width: 100%;
358
height: auto;
0
359
border-radius: 0.35rem;
360
}
361
+92
static/lightbox.js
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
let currentLightboxImages = [];
2
+
let currentLightboxIndex = 0;
3
+
4
+
function openLightbox(src) {
5
+
currentLightboxImages = [src];
6
+
currentLightboxIndex = 0;
7
+
showLightbox();
8
+
}
9
+
10
+
function openLightboxGroup(element) {
11
+
const group = element.closest('.img-group');
12
+
const images = Array.from(group.querySelectorAll('img')).map(img => img.src);
13
+
const clickedImg = element.querySelector('img');
14
+
15
+
currentLightboxImages = images;
16
+
currentLightboxIndex = images.indexOf(clickedImg.src);
17
+
showLightbox();
18
+
}
19
+
20
+
function showLightbox() {
21
+
let lightbox = document.getElementById('lightbox');
22
+
23
+
if (!lightbox) {
24
+
lightbox = document.createElement('div');
25
+
lightbox.id = 'lightbox';
26
+
lightbox.innerHTML = `
27
+
<div class="lightbox-content">
28
+
<button class="lightbox-close" onclick="closeLightbox()">×</button>
29
+
<img id="lightbox-img" src="" alt="">
30
+
<div class="lightbox-controls">
31
+
<button class="lightbox-prev" onclick="prevImage()">←</button>
32
+
<button class="lightbox-next" onclick="nextImage()">→</button>
33
+
</div>
34
+
</div>
35
+
`;
36
+
document.body.appendChild(lightbox);
37
+
38
+
lightbox.addEventListener('click', (e) => {
39
+
if (e.target === lightbox) closeLightbox();
40
+
});
41
+
42
+
document.addEventListener('keydown', handleKeyPress);
43
+
}
44
+
45
+
updateLightboxImage();
46
+
lightbox.style.display = 'flex';
47
+
document.body.style.overflow = 'hidden';
48
+
}
49
+
50
+
function closeLightbox() {
51
+
const lightbox = document.getElementById('lightbox');
52
+
if (lightbox) {
53
+
lightbox.style.display = 'none';
54
+
document.body.style.overflow = '';
55
+
}
56
+
}
57
+
58
+
function updateLightboxImage() {
59
+
const img = document.getElementById('lightbox-img');
60
+
const controls = document.querySelector('.lightbox-controls');
61
+
62
+
img.src = currentLightboxImages[currentLightboxIndex];
63
+
64
+
if (currentLightboxImages.length === 1) {
65
+
controls.style.display = 'none';
66
+
} else {
67
+
controls.style.display = 'flex';
68
+
}
69
+
}
70
+
71
+
function prevImage() {
72
+
currentLightboxIndex = (currentLightboxIndex - 1 + currentLightboxImages.length) % currentLightboxImages.length;
73
+
updateLightboxImage();
74
+
}
75
+
76
+
function nextImage() {
77
+
currentLightboxIndex = (currentLightboxIndex + 1) % currentLightboxImages.length;
78
+
updateLightboxImage();
79
+
}
80
+
81
+
function handleKeyPress(e) {
82
+
const lightbox = document.getElementById('lightbox');
83
+
if (!lightbox || lightbox.style.display !== 'flex') return;
84
+
85
+
if (e.key === 'Escape') {
86
+
closeLightbox();
87
+
} else if (e.key === 'ArrowLeft') {
88
+
prevImage();
89
+
} else if (e.key === 'ArrowRight') {
90
+
nextImage();
91
+
}
92
+
}
+7
templates/head.html
···
93
defer
94
></script>
95
0
0
0
0
0
0
0
96
<script type="speculationrules">
97
{
98
"prerender": [
···
93
defer
94
></script>
95
96
+
{% set lightboxJsHash = get_hash(path="lightbox.js", sha_type=256,
97
+
base64=true) %}
98
+
<script
99
+
src="{{ get_url(path='lightbox.js?' ~ lightboxJsHash, trailing_slash=false) | safe }}"
100
+
defer
101
+
></script>
102
+
103
<script type="speculationrules">
104
{
105
"prerender": [
+1
-1
templates/shortcodes/img.html
···
1
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
2
-
<div class="img-container">
3
<img src="{{id}}" {% if alt %}alt="{{alt}}" {% endif %} />
4
</div>
5
{% if caption %}
···
1
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
2
+
<div class="img-container" onclick="openLightbox('{{id}}')">
3
<img src="{{id}}" {% if alt %}alt="{{alt}}" {% endif %} />
4
</div>
5
{% if caption %}
+2
-2
templates/shortcodes/imgs.html
···
1
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
2
-
<div class="img-group">
3
{% set images = id | split(pat=",") %}
4
{% set alts = alt | default(value="") | split(pat=",") %}
5
{% for image in images %}
6
-
<div class="img-container">
7
<img src="{{image | trim}}" {% if alts[loop.index0] %}alt="{{alts[loop.index0] | trim}}" {% endif %} />
8
</div>
9
{% endfor %}
···
1
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
2
+
<div class="img-group" data-images="{{id}}" data-alts="{{alt | default(value='')}}">
3
{% set images = id | split(pat=",") %}
4
{% set alts = alt | default(value="") | split(pat=",") %}
5
{% for image in images %}
6
+
<div class="img-container" onclick="openLightboxGroup(this)">
7
<img src="{{image | trim}}" {% if alts[loop.index0] %}alt="{{alts[loop.index0] | trim}}" {% endif %} />
8
</div>
9
{% endfor %}