99
src/app.css
Normal file
@@ -0,0 +1,99 @@
|
||||
:root {
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
}
|
||||
|
||||
/* Explicit theme toggle overrides (win over media query when set) */
|
||||
html.dark {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
|
||||
html.light {
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.lightbox-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
column-count: 1;
|
||||
column-gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@media (width >= 768px) {
|
||||
.gallery-grid {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 1024px) {
|
||||
.gallery-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lightbox: not a Svelte component, styled globally for script.js target */
|
||||
#lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgb(0 0 0 / 90%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
#lightbox[aria-hidden='false'] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#lb-content img,
|
||||
#lb-content video {
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#lb-caption {
|
||||
color: #fff;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
#lb-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
13
src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
10
src/app.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 353 KiB |
|
Before Width: | Height: | Size: 52 KiB |
@@ -1,147 +0,0 @@
|
||||
:root {
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
}
|
||||
|
||||
/* Explicit theme toggle overrides (win over media query when set) */
|
||||
html.dark {
|
||||
--bg: #111;
|
||||
--fg: #eee;
|
||||
--accent: #46c;
|
||||
}
|
||||
|
||||
html.light {
|
||||
--bg: #fff;
|
||||
--fg: #222;
|
||||
--accent: #007acc;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.lightbox-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
column-count: 1;
|
||||
column-gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@media (width >= 768px) {
|
||||
.gallery-grid {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 1024px) {
|
||||
.gallery-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
aspect-ratio: 3/2;
|
||||
margin: 0 0 1rem;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gallery-item img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.gallery-item figcaption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.gallery-item:focus figcaption,
|
||||
.gallery-item:hover figcaption {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgb(0 0 0 / 90%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
#lightbox[aria-hidden='false'] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#lb-content img,
|
||||
#lb-content video {
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#lb-caption {
|
||||
color: #fff;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
#lb-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 896 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,11 +0,0 @@
|
||||
(function () {
|
||||
const script = document.currentScript;
|
||||
const id = script && script.getAttribute('data-ga-id');
|
||||
if (!id) return;
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(...args) {
|
||||
window.dataLayer.push(...args);
|
||||
}
|
||||
gtag('js', new Date());
|
||||
gtag('config', id, { anonymize_ip: true });
|
||||
})();
|
||||
@@ -1,120 +0,0 @@
|
||||
// --- theme toggle ---
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
const root = document.documentElement;
|
||||
const saved = window?.localStorage?.getItem('dark-mode');
|
||||
const sysDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (saved === 'true') {
|
||||
root.classList.add('dark');
|
||||
root.classList.remove('light');
|
||||
} else if (saved === 'false') {
|
||||
root.classList.add('light');
|
||||
root.classList.remove('dark');
|
||||
} else {
|
||||
if (sysDark) {
|
||||
root.classList.add('dark');
|
||||
root.classList.remove('light');
|
||||
} else {
|
||||
root.classList.add('light');
|
||||
root.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
toggle.addEventListener('click', () => {
|
||||
const isDark = root.classList.contains('dark');
|
||||
root.classList.toggle('dark', !isDark);
|
||||
root.classList.toggle('light', isDark);
|
||||
window?.localStorage?.setItem('dark-mode', !isDark);
|
||||
});
|
||||
|
||||
const { body } = document;
|
||||
|
||||
// --- lightbox base ---
|
||||
const lb = document.getElementById('lightbox');
|
||||
const lbCnt = document.getElementById('lb-content');
|
||||
const lbCap = document.getElementById('lb-caption');
|
||||
document.getElementById('lb-close').addEventListener('click', () => {
|
||||
lb.setAttribute('aria-hidden', 'true');
|
||||
body.classList.remove('lightbox-open');
|
||||
lbCnt.innerHTML = '';
|
||||
});
|
||||
|
||||
// --- build gallery ---
|
||||
const gallery = document.getElementById('gallery');
|
||||
const mediaData = JSON.parse(document.getElementById('media-data').textContent);
|
||||
|
||||
const createPicture = (item) => {
|
||||
const pic = document.createElement('picture');
|
||||
['desktop', 'tablet', 'mobile'].forEach((bp) => {
|
||||
const src = document.createElement('source');
|
||||
const widthQuery = bp === 'desktop' ? 1024 : bp === 'tablet' ? 768 : 0;
|
||||
src.media = `(min-width:${widthQuery}px)`;
|
||||
if (item.type === 'image') {
|
||||
src.srcset =
|
||||
`assets/media/${bp}/${item.name}@1x.webp 1x, ` +
|
||||
`assets/media/${bp}/${item.name}.webp 2x`;
|
||||
} else {
|
||||
// video poster still
|
||||
src.srcset =
|
||||
`assets/media/${bp}/${item.name}_still@1x.webp 1x, ` +
|
||||
`assets/media/${bp}/${item.name}_still.webp 2x`;
|
||||
}
|
||||
pic.appendChild(src);
|
||||
});
|
||||
|
||||
// thumbnail fallback (always 300px/2×)
|
||||
const img = document.createElement('img');
|
||||
img.src = `assets/media/thumbnail/${item.name}.webp`;
|
||||
img.alt = item.alt.replace(/[‘’]/g, '');
|
||||
img.height = item.height || undefined;
|
||||
img.width = item.width || undefined;
|
||||
img.loading = item.loading || 'lazy';
|
||||
img.fetchPriority = item.fetchpriority || undefined;
|
||||
pic.appendChild(img);
|
||||
return pic;
|
||||
};
|
||||
|
||||
mediaData.forEach((item) => {
|
||||
const fig = document.createElement('figure');
|
||||
fig.className = `gallery-item${item.type === 'video' ? ' video' : ''}`;
|
||||
fig.tabIndex = 0;
|
||||
fig.dataset.name = item.name;
|
||||
fig.dataset.type = item.type;
|
||||
fig.dataset.caption = item.caption;
|
||||
fig.appendChild(createPicture(item));
|
||||
|
||||
// overlay caption
|
||||
const cap = document.createElement('figcaption');
|
||||
cap.textContent = item.caption;
|
||||
fig.appendChild(cap);
|
||||
|
||||
// events
|
||||
fig.addEventListener('click', () => openLightbox(item));
|
||||
fig.addEventListener(
|
||||
'keypress',
|
||||
(e) => e.key === 'Enter' && openLightbox(item),
|
||||
);
|
||||
|
||||
gallery.appendChild(fig);
|
||||
});
|
||||
|
||||
// --- video toggle ---
|
||||
const videoTgl = document.getElementById('show_video');
|
||||
videoTgl.addEventListener('click', () => {
|
||||
openLightbox(mediaData.find((i) => i.type === 'video'));
|
||||
});
|
||||
|
||||
function openLightbox(item) {
|
||||
lbCnt.innerHTML = '';
|
||||
if (item.type === 'video') {
|
||||
const v = document.createElement('video');
|
||||
v.src = `assets/media/videos/${item.name}.mp4`;
|
||||
v.controls = true;
|
||||
v.autoplay = true;
|
||||
v.loading = item.loading || 'lazy';
|
||||
lbCnt.appendChild(v);
|
||||
} else {
|
||||
lbCnt.appendChild(createPicture(item));
|
||||
}
|
||||
lbCap.textContent = item.caption;
|
||||
body.classList.add('lightbox-open');
|
||||
lb.setAttribute('aria-hidden', 'false');
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 347 KiB |
|
Before Width: | Height: | Size: 826 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 903 KiB |
|
Before Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 637 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 630 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 494 KiB |
|
Before Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 618 KiB |
|
Before Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 294 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 315 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 373 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 1005 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 538 KiB |
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 748 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 800 KiB |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 770 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 826 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 688 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 423 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 51 KiB |