Compare commits
8 Commits
81e7417a1d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ba9a9dc9c4 | |||
| 9900b193f6 | |||
| 9b8f03d4ff | |||
| 9434c9f192 | |||
| 24733bca05 | |||
| c9a30a38d7 | |||
| be24904b78 | |||
| 3b359d2b37 |
+14
-2
@@ -1,5 +1,7 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@custom-variant hover (&:hover);
|
||||
|
||||
@theme {
|
||||
--color-paper: #f1ebe0;
|
||||
--color-paper-2: #e9e1d2;
|
||||
@@ -136,11 +138,11 @@ mark.hl { background: color-mix(in oklab, var(--roux) 22%, transparent); color:
|
||||
.lb { grid-template-columns: 1fr; grid-template-rows: auto 1fr auto auto; grid-template-areas: "topbar" "stage" "meta" "thumbs"; }
|
||||
}
|
||||
.lb-topbar { grid-area: topbar; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 22px; border-bottom: 1px solid rgba(236,231,221,.08); }
|
||||
.lb-brand { display: flex; align-items: center; gap: 14px; font: 500 11px/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; color: #c8c0b1; }
|
||||
.lb-brand { display: flex; align-items: center; gap: 14px; font: 500 11px/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; color: #c8c0b1; min-width: 0; overflow: hidden; }
|
||||
.lb-brand svg { height: 18px; }
|
||||
.lb-index { font: 500 11px/1 var(--mono); letter-spacing: .14em; color: #c8c0b1; font-variant-numeric: tabular-nums; }
|
||||
.lb-index b { color: #ece7dd; }
|
||||
.lb-close { width: 36px; height: 36px; border: 1px solid rgba(236,231,221,.18); border-radius: 50%; display: grid; place-items: center; color: #ece7dd; }
|
||||
.lb-close { width: 36px; height: 36px; flex-shrink: 0; border: 1px solid rgba(236,231,221,.18); border-radius: 50%; display: grid; place-items: center; color: #ece7dd; }
|
||||
.lb-close:hover { background: rgba(236,231,221,.08); }
|
||||
.lb-stage { grid-area: stage; position: relative; overflow: hidden; display: grid; place-items: center; padding: 24px; min-height: 0; }
|
||||
.lb-track { position: absolute; inset: 0; display: flex; transition: transform .55s cubic-bezier(.4,.0,.2,1); will-change: transform; }
|
||||
@@ -198,6 +200,16 @@ input[type="search"]::-webkit-search-cancel-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ── full-image zoom overlay ── */
|
||||
.img-zoom {
|
||||
position: fixed; inset: 0; z-index: 400;
|
||||
background: rgba(8,6,4,.97);
|
||||
display: none; place-items: center;
|
||||
cursor: zoom-out;
|
||||
}
|
||||
.img-zoom[data-open="true"] { display: grid; }
|
||||
.lb-img { cursor: zoom-in; }
|
||||
|
||||
/* ── scrollbar ── */
|
||||
::-webkit-scrollbar { width: 10px; height: 10px; }
|
||||
::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 0; }
|
||||
|
||||
+125
-14
@@ -2,6 +2,7 @@
|
||||
"htmlElements": {
|
||||
"tags": [
|
||||
"a",
|
||||
"article",
|
||||
"aside",
|
||||
"body",
|
||||
"br",
|
||||
@@ -18,6 +19,7 @@
|
||||
"h4",
|
||||
"head",
|
||||
"header",
|
||||
"hr",
|
||||
"html",
|
||||
"img",
|
||||
"input",
|
||||
@@ -25,36 +27,67 @@
|
||||
"link",
|
||||
"main",
|
||||
"meta",
|
||||
"nav",
|
||||
"p",
|
||||
"path",
|
||||
"picture",
|
||||
"polyline",
|
||||
"radialgradient",
|
||||
"script",
|
||||
"section",
|
||||
"source",
|
||||
"span",
|
||||
"stop",
|
||||
"sup",
|
||||
"strong",
|
||||
"svg",
|
||||
"title"
|
||||
],
|
||||
"classes": [
|
||||
"[&::-webkit-scrollbar]:hidden",
|
||||
"[&_a]:border-[var(--rule)]",
|
||||
"[&_a]:border-b",
|
||||
"[&_a]:duration-150",
|
||||
"[&_a]:text-ink-2",
|
||||
"[&_a]:transition-colors",
|
||||
"[&_b]:font-semibold",
|
||||
"[&_b]:mr-1",
|
||||
"[&_b]:text-ink",
|
||||
"[&_h2]:first:mt-0",
|
||||
"[&_h2]:font-medium",
|
||||
"[&_h2]:font-sans",
|
||||
"[&_h2]:leading-none",
|
||||
"[&_h2]:mb-4",
|
||||
"[&_h2]:mt-10",
|
||||
"[&_h2]:text-[10px]",
|
||||
"[&_h2]:text-ink-soft",
|
||||
"[&_h2]:tracking-[.22em]",
|
||||
"[&_h2]:uppercase",
|
||||
"[&_hr]:bg-[var(--rule-2)]",
|
||||
"[&_hr]:block",
|
||||
"[&_hr]:border-none",
|
||||
"[&_hr]:h-px",
|
||||
"[&_hr]:mb-0",
|
||||
"[&_hr]:mt-8",
|
||||
"[&_p]:font-serif",
|
||||
"[&_p]:leading-[1.72]",
|
||||
"[&_p]:mb-0",
|
||||
"[&_p]:mt-3",
|
||||
"[&_p]:text-[clamp(15px,1.05vw,17px)]",
|
||||
"[&_p]:text-ink-2",
|
||||
"[&_strong]:font-medium",
|
||||
"[&_strong]:font-sans",
|
||||
"[&_strong]:text-[13px]",
|
||||
"[&_strong]:text-ink",
|
||||
"[&_strong]:tracking-[.02em]",
|
||||
"[-webkit-box-orient:vertical]",
|
||||
"[-webkit-line-clamp:2]",
|
||||
"[background:color-mix(in_oklab,var(--paper)_86%,transparent)]",
|
||||
"[background:color-mix(in_oklab,var(--paper)_95%,transparent)]",
|
||||
"[display:-webkit-box]",
|
||||
"[filter:saturate(.92)_contrast(1.02)]",
|
||||
"[grid-template-columns:1fr_1px_auto]",
|
||||
"[grid-template-columns:1fr_auto_1fr]",
|
||||
"[grid-template-columns:2fr_1fr_1fr_1fr]",
|
||||
"[grid-template-columns:repeat(auto-fill,minmax(220px,1fr))]",
|
||||
"[grid-template-columns:repeat(auto-fill,minmax(280px,1fr))]",
|
||||
"[scrollbar-width:none]",
|
||||
"[transition-duration:.9s,.6s]",
|
||||
"[transition-timing-function:cubic-bezier(.2,.7,.1,1),ease]",
|
||||
"absolute",
|
||||
@@ -63,12 +96,13 @@
|
||||
"after:inset-0",
|
||||
"after:pointer-events-none",
|
||||
"after:shadow-[inset_0_0_0_1px_rgba(22,17,13,.04)]",
|
||||
"align-top",
|
||||
"aspect-[2/3]",
|
||||
"backdrop-blur-[14px]",
|
||||
"bg-[var(--paper)]",
|
||||
"bg-[var(--rule)]",
|
||||
"bg-current",
|
||||
"bg-ink",
|
||||
"bg-paper-2",
|
||||
"bg-roux",
|
||||
"bg-transparent",
|
||||
"block",
|
||||
"border",
|
||||
@@ -78,9 +112,11 @@
|
||||
"border-b",
|
||||
"border-current",
|
||||
"border-current/30",
|
||||
"border-ink",
|
||||
"border-l-0",
|
||||
"border-r",
|
||||
"border-t",
|
||||
"border-transparent",
|
||||
"border-white/20",
|
||||
"bottom-0",
|
||||
"bottom-[10px]",
|
||||
"card",
|
||||
@@ -94,6 +130,7 @@
|
||||
"flex",
|
||||
"flex-1",
|
||||
"flex-col",
|
||||
"flex-wrap",
|
||||
"font-display",
|
||||
"font-light",
|
||||
"font-medium",
|
||||
@@ -102,20 +139,36 @@
|
||||
"font-sans",
|
||||
"font-serif",
|
||||
"gap-1",
|
||||
"gap-1.5",
|
||||
"gap-10",
|
||||
"gap-2",
|
||||
"gap-3",
|
||||
"gap-4",
|
||||
"gap-5",
|
||||
"gap-6",
|
||||
"gap-[18px]",
|
||||
"gap-[7px]",
|
||||
"gap-[clamp(12px,2vw,24px)]",
|
||||
"gap-[var(--gap)]",
|
||||
"grid",
|
||||
"group",
|
||||
"group-hover:scale-[1.02]",
|
||||
"h-10",
|
||||
"h-6",
|
||||
"h-9",
|
||||
"h-[30px]",
|
||||
"h-full",
|
||||
"h-px",
|
||||
"hover:[&_a]:border-ink",
|
||||
"hover:[&_a]:text-ink",
|
||||
"hover:bg-roux-deep",
|
||||
"hover:bg-white/10",
|
||||
"hover:border-ink",
|
||||
"hover:opacity-100",
|
||||
"hover:text-ink",
|
||||
"hover:text-white",
|
||||
"img-zoom",
|
||||
"inline-flex",
|
||||
"inset-0",
|
||||
"inset-x-0",
|
||||
"italic",
|
||||
@@ -140,6 +193,7 @@
|
||||
"lb-track",
|
||||
"leading-[0.92]",
|
||||
"leading-[0.94]",
|
||||
"leading-[0.95]",
|
||||
"leading-[1.05]",
|
||||
"leading-[1.1]",
|
||||
"leading-[1.4]",
|
||||
@@ -162,37 +216,64 @@
|
||||
"max-[720px]:hidden",
|
||||
"max-[720px]:text-[15px]",
|
||||
"max-[820px]:[grid-template-columns:1fr_1fr]",
|
||||
"max-[820px]:block",
|
||||
"max-[820px]:border-[var(--rule)]",
|
||||
"max-[820px]:border-t",
|
||||
"max-[820px]:hidden",
|
||||
"max-h-[96vh]",
|
||||
"max-w-[14ch]",
|
||||
"max-w-[40ch]",
|
||||
"max-w-[44ch]",
|
||||
"max-w-[55ch]",
|
||||
"max-w-[62ch]",
|
||||
"max-w-[96vw]",
|
||||
"mb-10",
|
||||
"mb-2",
|
||||
"mb-4",
|
||||
"mb-5",
|
||||
"mb-6",
|
||||
"mb-8",
|
||||
"mb-[14px]",
|
||||
"min-h-[75vh]",
|
||||
"min-w-0",
|
||||
"mix-blend-difference",
|
||||
"ml-1",
|
||||
"ml-2",
|
||||
"ml-3",
|
||||
"mr-3",
|
||||
"mt-0.5",
|
||||
"mt-14",
|
||||
"mt-2",
|
||||
"mt-[14px]",
|
||||
"mt-[var(--gap)]",
|
||||
"mt-auto",
|
||||
"nav-link",
|
||||
"object-contain",
|
||||
"object-cover",
|
||||
"opacity-30",
|
||||
"opacity-50",
|
||||
"opacity-60",
|
||||
"opacity-[.035]",
|
||||
"outline-none",
|
||||
"overflow-hidden",
|
||||
"overflow-x-auto",
|
||||
"pb-0",
|
||||
"pb-14",
|
||||
"pb-[clamp(28px,3.5vw,48px)]",
|
||||
"pb-[clamp(32px,4vw,60px)]",
|
||||
"pb-px",
|
||||
"place-items-center",
|
||||
"placeholder:opacity-90",
|
||||
"placeholder:text-ink-soft",
|
||||
"pointer-events-none",
|
||||
"pt-4",
|
||||
"pt-9",
|
||||
"pt-[clamp(36px,5vw,64px)]",
|
||||
"px-[14px]",
|
||||
"pt-[clamp(40px,6vw,80px)]",
|
||||
"pt-[var(--gap)]",
|
||||
"px-4",
|
||||
"px-6",
|
||||
"px-[6px]",
|
||||
"px-[7px]",
|
||||
"px-[clamp(24px,4vw,60px)]",
|
||||
"px-[var(--pad)]",
|
||||
"py-1.5",
|
||||
"py-2",
|
||||
@@ -201,18 +282,24 @@
|
||||
"py-[14px]",
|
||||
"py-[3px]",
|
||||
"py-[5px]",
|
||||
"py-[clamp(24px,3vw,40px)]",
|
||||
"py-[clamp(32px,4vw,60px)]",
|
||||
"py-[clamp(36px,5vw,64px)]",
|
||||
"py-[clamp(48px,8vw,96px)]",
|
||||
"py-[var(--gap)]",
|
||||
"relative",
|
||||
"ribbon",
|
||||
"right-4",
|
||||
"right-[10px]",
|
||||
"rounded-[1px]",
|
||||
"rounded-[3px]",
|
||||
"rounded-full",
|
||||
"saturate-[1.05]",
|
||||
"searchpop",
|
||||
"select-none",
|
||||
"shrink-0",
|
||||
"space-y-0",
|
||||
"sticky",
|
||||
"tabs",
|
||||
"tabular-nums",
|
||||
"text-[10px]",
|
||||
"text-[11px]",
|
||||
@@ -222,17 +309,26 @@
|
||||
"text-[30px]",
|
||||
"text-[8.5px]",
|
||||
"text-[clamp(13px,1vw,16px)]",
|
||||
"text-[clamp(14px,1.1vw,17px)]",
|
||||
"text-[clamp(15px,1.2vw,18px)]",
|
||||
"text-[clamp(200px,40vw,520px)]",
|
||||
"text-[clamp(20px,1.6vw,26px)]",
|
||||
"text-[clamp(22px,2vw,30px)]",
|
||||
"text-[clamp(40px,6vw,80px)]",
|
||||
"text-[clamp(42px,6vw,88px)]",
|
||||
"text-[clamp(48px,6vw,80px)]",
|
||||
"text-[clamp(48px,7vw,96px)]",
|
||||
"text-[clamp(60px,8vw,110px)]",
|
||||
"text-center",
|
||||
"text-ink",
|
||||
"text-ink-2",
|
||||
"text-ink-soft",
|
||||
"text-ink-soft/40",
|
||||
"text-ink-soft/50",
|
||||
"text-paper",
|
||||
"text-roux",
|
||||
"text-white/60",
|
||||
"top-0",
|
||||
"top-4",
|
||||
"top-[10px]",
|
||||
"tracking-[.005em]",
|
||||
"tracking-[.045em]",
|
||||
@@ -245,23 +341,34 @@
|
||||
"tracking-[.22em]",
|
||||
"tracking-[.32em]",
|
||||
"tracking-normal",
|
||||
"transition-[color,border-color,background]",
|
||||
"transition-[transform,filter]",
|
||||
"transition-colors",
|
||||
"transition-opacity",
|
||||
"transition-transform",
|
||||
"translate-y-[6%]",
|
||||
"uppercase",
|
||||
"w-10",
|
||||
"w-12",
|
||||
"w-6",
|
||||
"w-8",
|
||||
"w-9",
|
||||
"w-[30px]",
|
||||
"w-full",
|
||||
"whitespace-nowrap",
|
||||
"z-10",
|
||||
"z-50",
|
||||
"z-[100]"
|
||||
],
|
||||
"ids": [
|
||||
"ai-training",
|
||||
"content",
|
||||
"copyright",
|
||||
"count",
|
||||
"grid",
|
||||
"hero",
|
||||
"imgZoom",
|
||||
"imgZoomClose",
|
||||
"imgZoomImg",
|
||||
"lb",
|
||||
"lbBloom",
|
||||
"lbIndex",
|
||||
@@ -271,12 +378,16 @@
|
||||
"masthead",
|
||||
"mhBloom",
|
||||
"mhDate",
|
||||
"photography",
|
||||
"publication",
|
||||
"responsible-person",
|
||||
"ribbon",
|
||||
"ribbonClose",
|
||||
"roux-data",
|
||||
"searchInput",
|
||||
"searchpop",
|
||||
"tabs"
|
||||
"technical",
|
||||
"viewFull"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,37 @@
|
||||
{{ define "main" }}
|
||||
{{- $issue := index (.Params.issues | default (slice "01")) 0 }}
|
||||
{{- $issueURL := printf "/issues/%s/" $issue }}
|
||||
{{- $img := .Resources.GetMatch "*.png" }}
|
||||
{{- $cardSrc := "" }}
|
||||
{{- if $img }}{{- $c := $img.Resize "900x1350 webp" }}{{- $cardSrc = $c.RelPermalink }}{{- end }}
|
||||
|
||||
<section id="hero" class="px-[var(--pad)] pt-[clamp(36px,5vw,64px)] pb-[clamp(28px,3.5vw,48px)] border-b border-[var(--rule)]">
|
||||
<div class="font-sans font-medium text-[10px] leading-none tracking-[.22em] uppercase text-ink-soft mb-4">
|
||||
№ {{ $issue }} · {{ index (.Params.categories | default (slice "Plate")) 0 }}
|
||||
</div>
|
||||
<h1 class="font-display font-normal text-[clamp(48px,7vw,96px)] leading-[0.94] m-0 mb-4"><em>{{ .Title }}</em></h1>
|
||||
<p class="font-serif italic text-[clamp(13px,1vw,16px)] leading-[1.65] text-ink-2 max-w-[55ch] m-0">
|
||||
<p class="font-serif italic text-[clamp(13px,1vw,16px)] leading-[1.65] text-ink-2 max-w-[55ch] m-0 mb-5">
|
||||
{{ .Params.description }}
|
||||
<a href="{{ $issueURL }}" class="border-b border-current ml-1">Return to issue →</a>
|
||||
</p>
|
||||
<div class="flex items-center gap-5 flex-wrap">
|
||||
<a href="{{ $issueURL }}"
|
||||
class="font-sans font-medium text-[11px] leading-none tracking-[.16em] uppercase text-ink-soft
|
||||
border-b border-[var(--rule)] pb-px transition-colors duration-200 hover:text-ink hover:border-ink">
|
||||
← Return to issue
|
||||
</a>
|
||||
{{- if $cardSrc }}
|
||||
<button id="viewFull" data-src="{{ $cardSrc }}"
|
||||
class="inline-flex items-center gap-[7px]
|
||||
font-sans font-medium text-[11px] leading-none tracking-[.16em] uppercase text-ink-soft
|
||||
border-b border-[var(--rule)] pb-px transition-colors duration-200 hover:text-ink hover:border-ink">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>
|
||||
</svg>
|
||||
View full image
|
||||
</button>
|
||||
{{- end }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="grid" data-density="default"
|
||||
|
||||
@@ -59,6 +59,23 @@
|
||||
{{- $posts | jsonify | safeJS }}
|
||||
</script>
|
||||
|
||||
{{/* Pure image zoom — opened by hero button or clicking the lightbox main image */}}
|
||||
<div id="imgZoom" class="img-zoom" role="dialog" aria-modal="true" aria-label="Full size image">
|
||||
<button id="imgZoomClose"
|
||||
class="absolute top-4 right-4 z-10
|
||||
w-10 h-10 grid place-items-center
|
||||
border border-white/20 rounded-full
|
||||
text-white/60 hover:text-white hover:bg-white/10
|
||||
transition-colors duration-200"
|
||||
aria-label="Close full image">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M18 6 6 18"/><path d="m6 6 12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
<img id="imgZoomImg" src="" alt="" class="max-w-[96vw] max-h-[96vh] object-contain select-none" />
|
||||
</div>
|
||||
|
||||
<script src="/js/app.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<div>
|
||||
<h4 class="font-sans font-medium text-[10px] leading-none tracking-[.22em] uppercase text-ink m-0 mb-[14px]">Colophon</h4>
|
||||
<p>Set in Italiana & Cormorant Garamond, with Outfit for typographic furniture. © Roux MMXXVI.</p>
|
||||
<p class="mt-[14px]">A <a href="https://pivoine.art" class="hover:text-ink border-b border-[var(--rule)]" target="_blank" rel="noopener">Pivoine</a> journal.</p>
|
||||
<p class="mt-[14px] text-ink">Press
|
||||
<span class="border border-[var(--rule)] px-[6px] py-[3px] rounded-[3px] font-mono text-[10px]">⌘K</span>
|
||||
from anywhere to search.
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
{{- end }}
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/assets/roux-mark.svg" />
|
||||
<link rel="apple-touch-icon" href="/assets/roux-mark.svg" />
|
||||
<link rel="mask-icon" href="/assets/roux-mark.svg" color="#8a3322" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<meta name="theme-color" content="#f1ebe0" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
+122
-4
@@ -20,6 +20,36 @@
|
||||
return esc(text).replace(re, '<mark class="hl">$1</mark>');
|
||||
}
|
||||
|
||||
function setMeta(attr, key, val) {
|
||||
let el = document.querySelector(`meta[${attr}="${key}"]`);
|
||||
if (val !== null && val !== undefined && val !== '') {
|
||||
if (!el) { el = document.createElement('meta'); el.setAttribute(attr, key); document.head.appendChild(el); }
|
||||
el.setAttribute('content', val);
|
||||
} else if (el) {
|
||||
el.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function syncHeadMeta(doc) {
|
||||
const canon = doc.querySelector('link[rel="canonical"]');
|
||||
const curCanon = document.querySelector('link[rel="canonical"]');
|
||||
if (canon && curCanon) curCanon.href = canon.href;
|
||||
[
|
||||
['name', 'description'],
|
||||
['property', 'og:title'],
|
||||
['property', 'og:description'],
|
||||
['property', 'og:url'],
|
||||
['property', 'og:image'],
|
||||
['property', 'og:image:width'],
|
||||
['property', 'og:image:height'],
|
||||
['name', 'twitter:card'],
|
||||
['name', 'twitter:image'],
|
||||
].forEach(([attr, key]) => {
|
||||
const src = doc.querySelector(`meta[${attr}="${key}"]`);
|
||||
setMeta(attr, key, src ? src.getAttribute('content') : null);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Inverted search index
|
||||
const INDEX = (() => {
|
||||
const map = new Map();
|
||||
@@ -211,6 +241,8 @@
|
||||
let lbIdx = -1; // index into lbList
|
||||
let lbBuilt = false;
|
||||
let lbReferrer = null; // URL to return to on close
|
||||
let imgZoomOpen = false;
|
||||
let imgZoomIdx = -1;
|
||||
|
||||
function lbOpen(slug, scopedList) {
|
||||
lbList = scopedList || POSTS;
|
||||
@@ -265,9 +297,22 @@
|
||||
if (lbIndex) {
|
||||
lbIndex.innerHTML = `<b>${String(lbIdx + 1).padStart(3,'0')}</b> / ${lbList.length}`;
|
||||
}
|
||||
lbBuildMeta(lbList[lbIdx]);
|
||||
const p = lbList[lbIdx];
|
||||
lbBuildMeta(p);
|
||||
syncThumbs();
|
||||
preloadNeighbors(lbIdx);
|
||||
if (p && smooth) {
|
||||
const postTitle = p.title + ' — Roux';
|
||||
const postUrl = new URL(p.url, location.origin).href;
|
||||
document.title = postTitle;
|
||||
history.replaceState({ slug: p.slug }, '', p.url);
|
||||
setMeta('name', 'description', p.description || null);
|
||||
setMeta('property', 'og:title', postTitle);
|
||||
setMeta('property', 'og:description', p.description || null);
|
||||
setMeta('property', 'og:url', postUrl);
|
||||
const canon = document.querySelector('link[rel="canonical"]');
|
||||
if (canon) canon.href = postUrl;
|
||||
}
|
||||
}
|
||||
|
||||
function preloadNeighbors(idx) {
|
||||
@@ -348,9 +393,9 @@
|
||||
});
|
||||
document.addEventListener('keydown', e => {
|
||||
if (lb.dataset.open !== 'true') return;
|
||||
if (e.key === 'Escape') { lbClose(); }
|
||||
if (e.key === 'ArrowLeft') { e.preventDefault(); goToSlide(lbIdx - 1); }
|
||||
if (e.key === 'ArrowRight') { e.preventDefault(); goToSlide(lbIdx + 1); }
|
||||
if (e.key === 'Escape') { if (imgZoomOpen) closeImgZoom(); else lbClose(); }
|
||||
if (e.key === 'ArrowLeft') { e.preventDefault(); imgZoomOpen ? goToZoomSlide(imgZoomIdx - 1) : goToSlide(lbIdx - 1); }
|
||||
if (e.key === 'ArrowRight') { e.preventDefault(); imgZoomOpen ? goToZoomSlide(imgZoomIdx + 1) : goToSlide(lbIdx + 1); }
|
||||
});
|
||||
|
||||
// Touch swipe
|
||||
@@ -375,6 +420,17 @@
|
||||
const scopedList = issueId ? POSTS.filter(p => p.issue === issueId) : POSTS;
|
||||
lbReferrer = location.href;
|
||||
history.pushState({ slug }, '', card.href);
|
||||
if (clicked) {
|
||||
const postTitle = clicked.title + ' — Roux';
|
||||
const postUrl = new URL(clicked.url, location.origin).href;
|
||||
document.title = postTitle;
|
||||
setMeta('name', 'description', clicked.description || null);
|
||||
setMeta('property', 'og:title', postTitle);
|
||||
setMeta('property', 'og:description', clicked.description || null);
|
||||
setMeta('property', 'og:url', postUrl);
|
||||
const canon = document.querySelector('link[rel="canonical"]');
|
||||
if (canon) canon.href = postUrl;
|
||||
}
|
||||
lbOpen(slug, scopedList.length ? scopedList : POSTS);
|
||||
});
|
||||
|
||||
@@ -429,6 +485,7 @@
|
||||
}
|
||||
|
||||
document.title = doc.title;
|
||||
syncHeadMeta(doc);
|
||||
history.pushState({}, '', url);
|
||||
|
||||
// Re-init page state
|
||||
@@ -469,4 +526,65 @@
|
||||
lbOpen(window.__ROUX_OPEN_SLUG, scoped.length ? scoped : POSTS);
|
||||
}
|
||||
|
||||
// ── Full-image zoom overlay
|
||||
const imgZoom = document.getElementById('imgZoom');
|
||||
const imgZoomImg = document.getElementById('imgZoomImg');
|
||||
|
||||
function openImgZoom(src) {
|
||||
if (!imgZoom || !imgZoomImg || !src) return;
|
||||
imgZoomImg.src = src;
|
||||
imgZoomIdx = lbIdx;
|
||||
imgZoom.dataset.open = 'true';
|
||||
imgZoomOpen = true;
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeImgZoom() {
|
||||
if (!imgZoom) return;
|
||||
imgZoom.dataset.open = 'false';
|
||||
imgZoomOpen = false;
|
||||
if (!lb || lb.dataset.open !== 'true') document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function goToZoomSlide(idx) {
|
||||
if (!lbList.length || !imgZoomImg) return;
|
||||
imgZoomIdx = Math.max(0, Math.min(idx, lbList.length - 1));
|
||||
const p = lbList[imgZoomIdx];
|
||||
if (p) imgZoomImg.src = p.card || p.thumb || '';
|
||||
goToSlide(imgZoomIdx);
|
||||
}
|
||||
|
||||
// Hero "View full image" button
|
||||
const viewFullBtn = document.getElementById('viewFull');
|
||||
if (viewFullBtn) {
|
||||
viewFullBtn.addEventListener('click', () => openImgZoom(viewFullBtn.dataset.src));
|
||||
}
|
||||
|
||||
// Click on the lightbox main image → zoom
|
||||
document.addEventListener('click', e => {
|
||||
const img = e.target.closest('.lb-img');
|
||||
if (img) openImgZoom(img.src);
|
||||
});
|
||||
|
||||
// Close on backdrop click or close button
|
||||
if (imgZoom) {
|
||||
imgZoom.addEventListener('click', e => { if (e.target === imgZoom) closeImgZoom(); });
|
||||
document.getElementById('imgZoomClose')?.addEventListener('click', closeImgZoom);
|
||||
|
||||
// Swipe to navigate
|
||||
let zoomTouchX = null;
|
||||
imgZoom.addEventListener('touchstart', e => { zoomTouchX = e.touches[0].clientX; }, { passive: true });
|
||||
imgZoom.addEventListener('touchend', e => {
|
||||
if (zoomTouchX === null) return;
|
||||
const dx = e.changedTouches[0].clientX - zoomTouchX;
|
||||
if (Math.abs(dx) > 50) dx < 0 ? goToZoomSlide(imgZoomIdx + 1) : goToZoomSlide(imgZoomIdx - 1);
|
||||
zoomTouchX = null;
|
||||
});
|
||||
}
|
||||
|
||||
// Escape when lightbox is NOT open (standalone zoom)
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape' && imgZoomOpen && (!lb || lb.dataset.open !== 'true')) closeImgZoom();
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Roux",
|
||||
"short_name": "Roux",
|
||||
"description": "A fashion journal in one hundred plates.",
|
||||
"icons": [
|
||||
{ "src": "/favicon-32.png", "type": "image/png", "sizes": "32x32" },
|
||||
{ "src": "/apple-touch-icon.png", "type": "image/png", "sizes": "180x180" },
|
||||
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
|
||||
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" },
|
||||
{ "src": "/assets/roux-mark.svg", "type": "image/svg+xml", "sizes": "any" }
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#f1ebe0",
|
||||
"theme_color": "#f1ebe0"
|
||||
}
|
||||
Reference in New Issue
Block a user