feat(home): htmx lazy-load cover images

Replace the eager <img> on the home page with an htmx placeholder <figure>
that fetches a /cover-fragment endpoint when it scrolls into view
(hx-trigger="revealed"), so heavy cover bytes load only on demand.
Add spinner + settle-fade transition CSS, and load htmx.min.js on home.
This commit is contained in:
Pontoporeia
2026-09-18 16:26:36 +02:00
parent 541470b9bb
commit fc66b37801
9 changed files with 366 additions and 6 deletions
+33
View File
@@ -117,6 +117,39 @@ class Dispatcher
};
}
// /cover-fragment (HTMX lazy-load: returns just the cover markup.
// Consumed by the home page's `revealed`-triggered placeholders — the
// heavy image bytes are fetched only when a card scrolls into view.
//
// Prefers the small WebP thumbnail (same storage-relative path, .webp
// ext) when present, else falls back to the original via <picture>.
if ($path === '/cover-fragment' || $path === '/cover-fragment.php') {
return static function (): void {
$coverPath = $_GET['path'] ?? '';
// Same strict whitelist as MediaController: no traversal, no junk.
if (
$coverPath === ''
|| !preg_match('#^[a-zA-Z0-9/_\-.]+$#', $coverPath)
) {
http_response_code(400);
exit;
}
$orig = '/media?path=' . urlencode($coverPath);
$webpPath = preg_replace('/\.[a-z0-9]+$/i', '.webp', $coverPath);
echo '<picture>'
. '<source type="image/webp" srcset="'
. htmlspecialchars('/media?path=' . urlencode((string) $webpPath), ENT_QUOTES, 'UTF-8')
. '">'
. '<img src="'
. htmlspecialchars($orig, ENT_QUOTES, 'UTF-8')
. '" alt="Couverture" loading="lazy">'
. '</picture>';
};
}
// /maintenance.php
if ($path === '/maintenance' || $path === '/maintenance.php') {
return function () {