(closest thing to native video lazy). * - Auto-fills missing width / height attributes (best CLS win). * - Excludes by substring patterns (src or class match) — useful for * hero banner classes, logo files, etc. * * Tier: Free per FEATURES.md "Images" §1-6 (LiteSpeed parity — all * Free in LS Cache). * * @package XSpeed */ declare(strict_types=1); namespace XSpeed\Modules\Lazy; defined( 'ABSPATH' ) || exit; use XSpeed\Lazy_Loader; use XSpeed\Module; final class LazyModule extends Module { public const SLUG = 'lazy'; public const TIER = self::TIER_FREE; public const VERSION = '1.0.0'; public function ui_metadata(): array { return array( 'label' => __( 'Media Optimization', 'xspeed' ), 'tab_label' => __( 'Lazy Loading', 'xspeed' ), // its own tab on the Media Optimization page 'icon' => 'Image', 'description' => __( 'Control how images, iframes, and videos load — lazy-loading, missing dimensions, and format optimization.', 'xspeed' ), // Host page: Lazy Loading (this module) + Image Optimization (Pro) // + AI Suggestions (Pro) as tabs — everything a page loads on one // page instead of separate rows (FBS-83633). Fonts is NOT here: it // has its own Optimization card (#86). 'custom_panel' => 'MediaPanel', ); } public function settings_schema(): array { return array( 'lazy_images' => array( 'type' => 'bool', 'default' => true, 'label' => __( 'Lazy-load Images', 'xspeed' ), 'description' => __( 'Add loading="lazy" + decoding="async" to tags in post content. The first images on the page get loading="eager" so the LCP image is not deferred.', 'xspeed' ), ), 'lazy_iframes' => array( 'type' => 'bool', 'default' => true, 'label' => __( 'Lazy-load Iframes', 'xspeed' ), 'description' => __( 'Add loading="lazy" to