feat(image): enhance image handling with focal points and responsive support
Deploy / verify (push) Failing after 14s
Deploy / deploy (push) Has been skipped

- Added support for extracting focal points and alt text from CMS image fields.
- Introduced responsive image handling in TopBanner component, allowing for AVIF/WebP sources and LQIP placeholders.
- Updated image transformation functions to accommodate focal point parameters for better cropping.
- Refactored image URL resolution logic across various components and routes to utilize new image field structure.
This commit is contained in:
Peter Meier
2026-04-18 00:42:33 +02:00
parent 4d79bf08da
commit a139372ce2
12 changed files with 734 additions and 61 deletions
+30 -1
View File
@@ -1,6 +1,11 @@
import type { PageServerLoad } from './$types';
import { getPageBySlug, getPages, getHomePageSlugFromConfig } from '$lib/cms';
import { resolveContentImages, resolveImageUrls } from '$lib/rusty-image';
import {
extractCmsImageField,
resolveContentImages,
resolveImageUrls,
} from '$lib/rusty-image';
import { buildResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import {
resolvePostOverviewBlocks,
resolveSearchableTextBlocks,
@@ -8,6 +13,13 @@ import {
} from '$lib/blog-utils';
import { DEFAULT_HOME_PAGE_SLUG, PAGE_RESOLVE, SITE_NAME } from '$lib/constants';
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920, 2400];
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)}%`;
}
export const load: PageServerLoad = async ({ locals }) => {
const translations = locals.translations ?? {};
let homeSlug = DEFAULT_HOME_PAGE_SLUG;
@@ -42,6 +54,8 @@ export const load: PageServerLoad = async ({ locals }) => {
let topBanner: {
banner: import('$lib/cms').FullwidthBannerEntry | null;
resolvedImages: string[];
responsive: ResponsiveImage | null;
focalCss: string | null;
headline?: string;
subheadline?: string;
} | null = null;
@@ -54,7 +68,20 @@ export const load: PageServerLoad = async ({ locals }) => {
? [(bannerRaw as { image?: unknown }).image]
: [];
let resolvedImages: string[] = [];
let responsive: ResponsiveImage | null = null;
let focalCss: string | null = null;
if (bannerImages.length > 0) {
const primary = extractCmsImageField(bannerImages[0]);
if (primary?.url) {
focalCss = focalToCss(primary.focal);
responsive = await buildResponsiveImage(primary.url, {
widths: BANNER_WIDTHS,
fit: 'contain',
formats: ['avif', 'webp', 'jpeg'],
quality: 78,
focal: primary.focal,
});
}
resolvedImages = await resolveImageUrls(bannerImages as string[], {
width: 2000,
height: 750,
@@ -65,6 +92,8 @@ export const load: PageServerLoad = async ({ locals }) => {
topBanner = {
banner: bannerRaw as import('$lib/cms').FullwidthBannerEntry | null,
resolvedImages,
responsive,
focalCss,
headline: page.headline ?? undefined,
subheadline: page.subheadline ?? undefined,
};