perf: LCP-Fix, Banner-Crops, SSR-Cache, Bulk-Windkarte
Deploy / verify (push) Successful in 1m3s
Deploy / deploy (push) Successful in 1m47s

- TopBanner: JS-Fade-Gate entfernt (opacity-0 bis Hydration schob LCP
  von ~3s auf ~8.6s) — Bild malt jetzt progressiv über den LQIP
- buildBannerResponsive: Breakpoint-Varianten (1/1, 16/9, 24/9) mit
  media-Attributen, quality 60; CMS braucht explizites h neben ar,
  sonst kein Crop (Mobile bekam 1.92:1 unscharf hochskaliert)
- Layout-Preload: ein Link pro Breakpoint-Variante (WebP)
- Layout-Bootstrap-Batch + Logo-SVG-Fetch TTL-gecacht (liefen pro
  SSR-Request); invalidateCollection versteht CSV-Key-Heads
- WindkarteBlock: 40 Einzel-Fetches -> 1 List-Request mit Filter
- app.html: preconnect cms.pm86.de, api.iconify.design, analytics
- u-url Microformat: echte canonicalUrl statt href="#" (SEO-Audit)
- Icon-Subset: 'cloud' ergänzt (kein Runtime-Fetch an iconify mehr)
- StrommixBlock: nutzloses transition-colors entfernt (CLS-Audit)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-06-12 09:50:01 +02:00
parent 41597f87b5
commit d9519a867e
13 changed files with 164 additions and 89 deletions
+2 -11
View File
@@ -6,7 +6,7 @@ import {
resolveContentImages,
resolveImageUrls,
} from '$lib/rusty-image';
import { buildResponsiveImage, warmResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import { buildBannerResponsive, warmResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import {
resolvePostOverviewBlocks,
resolveSearchableTextBlocks,
@@ -20,9 +20,6 @@ import { env } from '$env/dynamic/public';
import { error } from '@sveltejs/kit';
import { sanitizeBlocks } from '$lib/sanitize-blocks.server';
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920];
const BANNER_AR = '16/9';
function focalToCss(focal: { x: number; y: number } | undefined): string | null {
if (!focal) return null;
return `${(focal.x * 100).toFixed(2)}% ${(focal.y * 100).toFixed(2)}%`;
@@ -106,13 +103,7 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
const primary = extractCmsImageField(imageArr[0]);
if (primary?.url) {
topBannerFocalCss = focalToCss(primary.focal);
topBannerResponsive = await buildResponsiveImage(primary.url, {
widths: BANNER_WIDTHS,
fit: 'cover',
ar: BANNER_AR,
quality: 72,
focal: primary.focal,
});
topBannerResponsive = await buildBannerResponsive(primary.url, primary.focal);
const socialRel = ensureTransformedImage(primary.url, {
...POST_SOCIAL_IMAGE_TRANSFORM,
focalX: primary.focal?.x,