Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ All notable changes to the Nynaeve theme will be documented in this file.

For project-wide changes (infrastructure, tooling, cross-cutting concerns), see the [project root CHANGELOG.md](../../../../../CHANGELOG.md).

## [2.0.15] - 2025-11-24

### Added
- **Speed Optimization: Responsive Hero Images** ([HeroBlock.php:181-196](app/Blocks/HeroBlock.php#L181-L196), [setup.php:149-151](app/setup.php#L149-L151))
- Added custom image sizes `hero-desktop` (600×348) and `hero-desktop-2x` (1200×696) for hero block
- Hero block now uses `wp_get_attachment_image()` with automatic srcset generation
- Browser loads appropriately sized image based on display and viewport
- **Impact**: ~50 KiB savings on standard displays, ~20 KiB on retina (from 69.8 KiB original)
- **Note**: Run `wp media regenerate --only-missing` after deploying to create new sizes

### Changed
- Updated `hero-block.blade.php` to use responsive `$desktop_image_html` with URL fallback for preview mode

### Documentation
- Updated `docs/nynaeve/SPEED-TWEAKS.md` with Optimization 7 implementation details

## [2.0.14] - 2025-11-24

### Added
Expand Down
40 changes: 38 additions & 2 deletions app/Blocks/HeroBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,42 @@ public function with(): array
$sub_heading_text_value = 'Website & Ecommerce Solutions for SME';
}

$desktop_image_url_value = $desktop_image['url'] ?? null;
// Generate responsive image HTML for desktop
// Uses 'hero-desktop' size (600x348) with srcset for retina (1200x696)
$desktop_image_html = '';
$desktop_image_alt_value = $desktop_image['alt'] ?? ($desktop_image['title'] ?? 'Desktop image');
$mobile_image_url_value = $mobile_image['url'] ?? null;
if (! empty($desktop_image['ID'])) {
$desktop_image_html = wp_get_attachment_image(
$desktop_image['ID'],
'hero-desktop',
false,
[
'class' => 'desktop-only-img object-cover w-full h-full',
'loading' => 'lazy',
'sizes' => '(min-width: 1024px) 576px, 50vw',
]
);
}

// Generate responsive image HTML for mobile
// Uses 'medium' size with automatic srcset
$mobile_image_html = '';
$mobile_image_alt_value = $mobile_image['alt'] ?? ($mobile_image['title'] ?? 'Mobile image');
if (! empty($mobile_image['ID'])) {
$mobile_image_html = wp_get_attachment_image(
$mobile_image['ID'],
'medium',
false,
[
'class' => 'object-cover',
'loading' => 'lazy',
]
);
}

// Fallback URLs for preview mode or legacy template support
$desktop_image_url_value = $desktop_image['url'] ?? null;
$mobile_image_url_value = $mobile_image['url'] ?? null;

if ($is_preview_mode) {
if (! $desktop_image_url_value) {
Expand All @@ -190,21 +222,25 @@ public function with(): array
// Base64 encode instead of using urlencode
$desktop_image_url_value = 'data:image/svg+xml;base64,'.base64_encode($desktop_svg);
$desktop_image_alt_value = 'Placeholder for desktop image';
$desktop_image_html = ''; // Clear HTML so URL fallback is used
}
if (! $mobile_image_url_value) {
// Create mobile placeholder SVG
$mobile_svg = '<svg width="800" height="800" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" preserveAspectRatio="none"><rect width="800" height="800" fill="#e0e0e0"/><path d="M800 800L0 0" stroke="#969696" stroke-width="2" vector-effect="non-scaling-stroke" fill="none"/><text x="400" y="400" text-anchor="middle" fill="#969696" font-family="system-ui, -apple-system, sans-serif" font-size="32">Mobile Image (1:1)</text></svg>';
// Base64 encode instead of using urlencode
$mobile_image_url_value = 'data:image/svg+xml;base64,'.base64_encode($mobile_svg);
$mobile_image_alt_value = 'Placeholder for mobile/tablet image';
$mobile_image_html = ''; // Clear HTML so URL fallback is used
}
}

return [
'heading_text' => $heading_text_value,
'sub_heading_text' => $sub_heading_text_value,
'desktop_image_html' => $desktop_image_html,
'desktop_image_url' => $desktop_image_url_value,
'desktop_image_alt' => $desktop_image_alt_value,
'mobile_image_html' => $mobile_image_html,
'mobile_image_url' => $mobile_image_url_value,
'mobile_image_alt' => $mobile_image_alt_value,
'font_weight' => $font_weight_value,
Expand Down
4 changes: 4 additions & 0 deletions app/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@
add_action('after_setup_theme', function () {
add_image_size('product-gallery-main', 800, 1000, true); // 800x1000 pixels, cropped
add_image_size('portfolio-slide', 800, 500, true); // 16:10 aspect ratio, good for both desktop and mobile

// Hero block responsive images (displayed at ~576x334 on desktop)
add_image_size('hero-desktop', 600, 348, true); // 1x for standard displays
add_image_size('hero-desktop-2x', 1200, 696, true); // 2x for retina displays
}, 20);

/**
Expand Down
33 changes: 16 additions & 17 deletions resources/views/blocks/hero-block.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,15 @@
<div class="hero-image-column">
{{-- Desktop Image: Only add the actual img on desktop or in admin --}}
<div class="editor-desktop-image">
@if (!empty($desktop_image_url))
{{-- Only load desktop image on larger screens or in admin --}}
@if(is_admin())
<img src="{{ $desktop_image_url }}"
alt="{{ $desktop_image_alt }}"
loading="lazy"
class="object-cover w-full h-full">
@else
{{-- Use a data attribute to store the URL, which will be loaded via JS only on desktop --}}
<img src="{{ $desktop_image_url }}"
alt="{{ $desktop_image_alt }}"
loading="lazy"
class="desktop-only-img object-cover w-full h-full">
@endif
@if (!empty($desktop_image_html))
{{-- Responsive image with srcset for optimal loading --}}
{!! $desktop_image_html !!}
@elseif (!empty($desktop_image_url))
{{-- Fallback for preview mode or missing responsive sizes --}}
<img src="{{ $desktop_image_url }}"
alt="{{ $desktop_image_alt }}"
loading="lazy"
class="desktop-only-img object-cover w-full h-full">
@elseif($is_preview)
<div class="h-full w-full bg-gray-200 flex items-center justify-center rounded-lg text-gray-500">
Desktop Image Placeholder (16:9)
Expand All @@ -56,9 +51,13 @@ class="desktop-only-img object-cover w-full h-full">

{{-- Mobile/Tablet Image: Visible on screens smaller than lg --}}
<div class="editor-mobile-image">
@if (!empty($mobile_image_url))
<img src="{{ $mobile_image_url }}"
alt="{{ $mobile_image_alt }}"
@if (!empty($mobile_image_html))
{{-- Responsive image with srcset for optimal loading --}}
{!! $mobile_image_html !!}
@elseif (!empty($mobile_image_url))
{{-- Fallback for preview mode or missing responsive sizes --}}
<img src="{{ $mobile_image_url }}"
alt="{{ $mobile_image_alt }}"
loading="lazy"
class="object-cover">
@elseif($is_preview)
Expand Down
2 changes: 1 addition & 1 deletion style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Theme Name: Nynaeve
Theme URI: https://imagewize.com
Description: Modern WordPress theme built on Sage 11 with reusable custom blocks using WordPress native tools and the Roots.io stack.
Version: 2.0.14
Version: 2.0.15
Author: Jasper Frumau
Author URI: https://magewize.com
Text Domain: nynaeve
Expand Down