Add responsive image optimization with WebP support

- Configure thumbs presets in config.php (quality 85%, WebP format)
- Create picture snippet with srcset and WebP support
- Add product-card and product-detail presets
- Update templates to use optimized images
- Implement native lazy loading for product cards
- Add aspect-ratio CSS for layout stability

Improvements over reference project:
- Complete PHPDoc documentation
- Native CSS aspect-ratio support
- Optimized lazy loading strategy
- Product-specific image presets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2025-12-10 16:00:58 +01:00
parent 9c91a87720
commit 30d09d6104
4 changed files with 101 additions and 2 deletions

View file

@ -5,6 +5,37 @@ return [
'languages' => true,
'thumbs' => [
'quality' => 85,
'format' => 'webp',
'presets' => [
'product-card' => [
'width' => 600,
'height' => 600,
'crop' => true,
'format' => 'webp'
],
'product-detail' => [
'width' => 1200,
'format' => 'webp'
],
'default' => [
'width' => 1024,
'format' => 'webp'
],
],
'srcsets' => [
'default' => [400, 600, 800, 1024, 1440, 2048],
'webp' => [
'400w' => ['width' => 400, 'format' => 'webp'],
'600w' => ['width' => 600, 'format' => 'webp'],
'800w' => ['width' => 800, 'format' => 'webp'],
'1024w' => ['width' => 1024, 'format' => 'webp'],
'1440w' => ['width' => 1440, 'format' => 'webp'],
],
],
],
'routes' => [
[
'pattern' => 'snipcart-webhook',

56
site/snippets/picture.php Normal file
View file

@ -0,0 +1,56 @@
<?php
/**
* Picture Snippet - Optimized image output with responsive srcset and WebP support
*
* @param Kirby\Cms\File $file - Required: The image file object
* @param string $alt - Optional: Alt text (defaults to file's alt field)
* @param string $class - Optional: CSS class for the picture element
* @param string|int $size - Optional: Size hint in vw for the sizes attribute (e.g., 50 for 50vw)
* @param bool $lazy - Optional: Enable native lazy loading (default: true)
* @param string $preset - Optional: Thumb preset to use (default: 'default')
* @param bool $crop - Optional: Crop the image (default: false)
*/
if (!isset($file) || !$file) return;
// Configuration
$alt = $alt ?? $file->alt()->or($file->filename())->value();
$class = isset($class) ? 'class="' . $class . '"' : '';
$lazy = $lazy ?? true;
$preset = $preset ?? 'default';
// Sizes attribute - determines which image size the browser should load
if (isset($size)) {
$sizes = is_numeric($size)
? "(min-width: 1024px) {$size}vw, 100vw"
: $size;
} else {
$sizes = "(min-width: 1024px) 50vw, 100vw";
}
// Generate srcsets for WebP and fallback
$webpSrcset = $file->srcset('webp');
$srcset = $file->srcset('default');
// Get optimized source URL
$src = $file->thumb($preset)->url();
// Get dimensions to avoid layout shift
$width = $file->width();
$height = $file->height();
$aspectRatio = $height > 0 ? ($height / $width) * 100 : 0;
?>
<picture <?= $class ?> data-id="<?= $file->uuid() ?>">
<source srcset="<?= $webpSrcset ?>" sizes="<?= $sizes ?>" type="image/webp">
<img
src="<?= $src ?>"
srcset="<?= $srcset ?>"
sizes="<?= $sizes ?>"
width="<?= $width ?>"
height="<?= $height ?>"
alt="<?= $alt ?>"
<?= $lazy ? 'loading="lazy"' : '' ?>
style="aspect-ratio: <?= $width ?> / <?= $height ?>;"
>
</picture>

View file

@ -10,7 +10,13 @@
<article class="store__product">
<figure>
<?php if($image = $product->images()->first()): ?>
<img src="<?= $image->url() ?>" alt="<?= $product->title()->html() ?>" />
<?php snippet('picture', [
'file' => $image,
'alt' => $product->title()->html(),
'preset' => 'product-card',
'size' => 25,
'lazy' => true
]) ?>
<?php endif ?>
</figure>
<p class="line-1"><a href="<?= $product->url() ?>"><?= $product->title()->html() ?></a></p>

View file

@ -70,7 +70,13 @@
<figure>
<?php if($image = $page->images()->first()): ?>
<img src="<?= $image->url() ?>" alt="<?= $page->title()->html() ?>" />
<?php snippet('picture', [
'file' => $image,
'alt' => $page->title()->html(),
'preset' => 'product-detail',
'size' => 50,
'lazy' => false
]) ?>
<?php endif ?>
</figure>
</section>