Files
pivoine.art/layouts/_default/baseof.html
T
valknarandClaude Sonnet 5 5889f02c21 fix: replace deprecated .Site.LanguageCode with .Site.Language.Locale
Hugo v0.158.0 deprecated .Site.LanguageCode in favor of .Site.Language.Locale,
and deprecated the languageCode config key in favor of locale. Update both to
silence the build warnings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8Es4ZhoJG6vrxN3GLGhLz
2026-09-13 17:48:58 +02:00

178 lines
6.7 KiB
HTML

<!doctype html>
<html lang="{{ .Site.Language.Locale }}" class="scroll-smooth">
<head>
{{- partial "head.html" . -}}
</head>
<body
class="bg-void text-paper font-body min-h-screen flex flex-col antialiased"
hx-boost="true"
hx-select="#main-content"
hx-target="#main-content"
hx-swap="outerHTML"
hx-push-url="true"
>
<!-- HTMX transition progress bar -->
<div id="progress-bar" aria-hidden="true"></div>
{{- partial "nav.html" . -}}
{{- block "page-background" . -}}{{- end -}}
<main id="main-content" class="flex-1" hx-history-elt>
{{- block "main" . }}{{- end }}
</main>
{{- partial "footer.html" . -}}
<!-- ── Global lightbox — lives outside #main-content so HTMX never touches it -->
<div
x-data="{
open: false,
idx: 0,
fill: false,
items: [],
close() { this.open = false; this.fill = false },
prev() { this.idx = (this.idx - 1 + this.items.length) % this.items.length },
next() { this.idx = (this.idx + 1) % this.items.length }
}"
@lightbox:open.window="items = $event.detail.items; idx = $event.detail.idx; open = true"
@lightbox:close.window="close()"
@keydown.escape.window="open && close()"
@keydown.arrow-left.window="open && prev()"
@keydown.arrow-right.window="open && next()"
@keydown.f.window="open && (fill = !fill)"
x-show="open"
x-cloak
x-transition:enter="transition duration-200 ease-out"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition duration-150 ease-in"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="fixed inset-0 z-[200] flex items-center justify-center bg-void/95 backdrop-blur-sm"
@click.self="close()"
role="dialog"
aria-modal="true"
aria-label="Media lightbox"
>
<!-- Top chrome bar -->
<div
class="absolute top-0 inset-x-0 z-10 flex items-center justify-between px-2 h-14"
style="background: linear-gradient(to bottom, rgba(5,5,16,0.85), transparent)"
>
<div class="absolute top-0 inset-x-0 h-px gradient-line"></div>
<div class="w-24"></div>
<div
class="label text-fog tabular-nums"
x-text="items.length ? `${String(idx + 1).padStart(2,'0')} / ${String(items.length).padStart(2,'0')}` : ''"
></div>
<div class="flex items-center w-24 justify-end gap-1">
<button
@click="fill = !fill"
class="w-10 h-10 flex items-center justify-center label transition-colors"
:class="fill ? 'text-heat hover:text-chalk' : 'text-fog hover:text-heat'"
:aria-label="fill ? 'Switch to fit mode' : 'Switch to fill mode'"
x-text="fill ? 'FIT' : 'FILL'"
></button>
<button
@click="close()"
class="w-10 h-10 flex items-center justify-center label text-fog hover:text-chalk transition-colors"
aria-label="Close"
>
</button>
</div>
</div>
<!-- Prev -->
<button
@click="prev()"
x-show="items.length > 1"
class="absolute left-2 top-1/2 -translate-y-1/2 z-10 w-10 h-10 md:w-12 md:h-12 flex items-center justify-center text-fog hover:text-heat transition-colors text-xl"
aria-label="Previous"
>
{{- partial "icon.html" "arrow-left" -}}
</button>
<!-- Media -->
<div
class="absolute inset-0 flex items-center justify-center transition-[padding] duration-300"
:class="fill ? 'p-0' : 'pt-14 pb-14 px-14 md:px-20'"
>
<video
x-show="items[idx] && items[idx].video"
:src="items[idx] && items[idx].video ? items[idx].video : ''"
:poster="items[idx] ? items[idx].img : ''"
x-effect="if (open && items[idx] && items[idx].video) { $el.load(); $el.play() }"
:class="fill ? 'w-full h-full object-cover' : 'max-w-full max-h-full object-contain'"
class="transition-all duration-300"
controls
loop
muted
playsinline
></video>
<img
x-show="items[idx] && !items[idx].video"
:src="items[idx] && !items[idx].video ? items[idx].img : ''"
alt=""
:class="fill ? 'w-full h-full object-cover' : 'max-w-full max-h-full object-contain'"
class="transition-all duration-300"
/>
</div>
<!-- Next -->
<button
@click="next()"
x-show="items.length > 1"
class="absolute right-2 top-1/2 -translate-y-1/2 z-10 w-10 h-10 md:w-12 md:h-12 flex items-center justify-center text-fog hover:text-heat transition-colors text-xl"
aria-label="Next"
>
{{- partial "icon.html" "arrow-right" -}}
</button>
<!-- Dots -->
<div
x-show="items.length > 1"
class="absolute bottom-0 inset-x-0 z-10 flex items-center justify-center h-14 gap-2"
style="background: linear-gradient(to top, rgba(5,5,16,0.85), transparent)"
>
<div class="absolute bottom-0 inset-x-0 h-px gradient-line"></div>
<template x-for="(item, i) in items" :key="i">
<button
@click="idx = i"
class="h-1.5 rounded-full transition-all duration-300"
:class="i === idx ? 'w-5 bg-heat' : 'w-1.5 bg-fog hover:bg-chalk'"
:aria-label="`Go to item ${i + 1}`"
></button>
</template>
</div>
</div>
<!-- HTMX (page transitions & progressive enhancement) -->
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script>
<!-- Alpine.js store initialisation (must run before alpine:init) -->
<script>
document.addEventListener("alpine:init", () => {
Alpine.store("nav", { path: window.location.pathname, open: false });
});
</script>
<!-- Alpine.js (reactive UI — nav, interactions) -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script>
<!-- Site JS -->
{{- $js := resources.Get "js/main.js" -}}
{{- if eq hugo.Environment "production" -}}
{{- $js = $js | minify | fingerprint "sha256" -}}
{{- end -}}
<script
src="{{ $js.RelPermalink }}"
{{ if eq hugo.Environment "production" }}integrity="{{ $js.Data.Integrity }}"{{ end }}
defer
></script>
{{- block "scripts" . }}{{- end }}
{{- if and (eq hugo.Environment "production") .Site.Params.umamiId -}}
<!-- Umami analytics — cookie-free, self-hosted -->
<script defer src="{{ .Site.Params.umamiSrc }}" data-website-id="{{ .Site.Params.umamiId }}"></script>
{{- end -}}
</body>
</html>