diff --git a/projects/plugins/jetpack/changelog/update-featured-images-fallback-add-srcset b/projects/plugins/jetpack/changelog/update-featured-images-fallback-add-srcset
new file mode 100644
index 0000000000000..89340a389dbeb
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/update-featured-images-fallback-add-srcset
@@ -0,0 +1,4 @@
+Significance: patch
+Type: enhancement
+
+Featured images fallback: add srcset
diff --git a/projects/plugins/jetpack/class.jetpack-post-images.php b/projects/plugins/jetpack/class.jetpack-post-images.php
index e31455bed1667..40474b48197ed 100644
--- a/projects/plugins/jetpack/class.jetpack-post-images.php
+++ b/projects/plugins/jetpack/class.jetpack-post-images.php
@@ -765,9 +765,10 @@ public static function get_images( $post_id, $args = array() ) {
* @param array $image Array containing details of the image.
* @param int $base_width Base image width (i.e., the width at 1x).
* @param int $base_height Base image height (i.e., the height at 1x).
+ * @param bool $use_widths Whether to generate the srcset with widths instead of multipliers.
* @return string The srcset for the image.
*/
- public static function generate_cropped_srcset( $image, $base_width, $base_height ) {
+ public static function generate_cropped_srcset( $image, $base_width, $base_height, $use_widths = false ) {
$srcset = '';
if ( ! is_array( $image ) || empty( $image['src'] ) || empty( $image['src_width'] ) ) {
@@ -783,12 +784,17 @@ public static function generate_cropped_srcset( $image, $base_width, $base_heigh
break;
}
- $srcset_url = self::fit_image_url(
+ $srcset_url = self::fit_image_url(
$image['src'],
$srcset_width,
$srcset_height
);
- $srcset_values[] = "{$srcset_url} {$multiplier}x";
+
+ if ( $use_widths ) {
+ $srcset_values[] = "{$srcset_url} {$srcset_width}w";
+ } else {
+ $srcset_values[] = "{$srcset_url} {$multiplier}x";
+ }
}
if ( count( $srcset_values ) > 1 ) {
diff --git a/projects/plugins/jetpack/modules/theme-tools/content-options/featured-images-fallback.php b/projects/plugins/jetpack/modules/theme-tools/content-options/featured-images-fallback.php
index 5140914fa5096..59c6e7dd48f31 100644
--- a/projects/plugins/jetpack/modules/theme-tools/content-options/featured-images-fallback.php
+++ b/projects/plugins/jetpack/modules/theme-tools/content-options/featured-images-fallback.php
@@ -58,12 +58,66 @@ function jetpack_featured_images_fallback_get_image( $html, $post_id, $post_thum
$image['crop'] = $_wp_additional_image_sizes[ $size ]['crop'];
}
- $image_src = Jetpack_PostImages::fit_image_url( $image['src'], $image['width'], $image['height'] );
+ // Force `crop` to be a simple boolean, to avoid dealing with WP crop positions.
+ $image['crop'] = boolval( $image['crop'] );
+
+ $image_sizes = '';
+
+ if ( $image['src_width'] && $image['src_height'] && $image['width'] && $image['height'] ) {
+ $width = intval( $image['width'] );
+ $height = intval( $image['height'] );
+ $src_width = intval( $image['src_width'] );
+ $src_height = intval( $image['src_height'] );
+
+ // If we're aware of the source dimensions, calculate the final image height and width.
+ // This allows us to provide them as attributes in the `` tag, to avoid layout shifts.
+ // It also allows us to calculate a `srcset`.
+ if ( $image['crop'] ) {
+ // If we're cropping, the final dimensions are calculated independently of each other.
+ $width = min( $width, $src_width );
+ $height = min( $height, $src_height );
+ } else {
+ // If we're not cropping, we need to preserve aspect ratio.
+ $dims = wp_constrain_dimensions( $src_width, $src_height, $width, $height );
+ $width = $dims[0];
+ $height = $dims[1];
+ }
+
+ $image_src = Jetpack_PostImages::fit_image_url( $image['src'], $width, $height );
+ $image_srcset = Jetpack_PostImages::generate_cropped_srcset( $image, $width, $height, true );
+ $image_sizes = 'min(' . $width . 'px, 100vw)';
+ } else {
+ // If we're not aware of the source dimensions, leave the size calculations to the CDN, and
+ // fall back to a simpler `` tag without `width`/`height` or `srcset`.
+ $image_src = Jetpack_PostImages::fit_image_url( $image['src'], $image['width'], $image['height'] );
+
+ // Use the theme's crop setting rather than forcing to true.
+ $image_src = add_query_arg( 'crop', $image['crop'], $image_src );
+ }
- // Use the theme's crop setting rather than forcing to true.
- $image_src = add_query_arg( 'crop', $image['crop'], $image_src );
+ $default_attr = array(
+ 'srcset' => $image_srcset,
+ 'sizes' => $image_sizes,
+ 'loading' => is_singular() ? 'eager' : 'lazy',
+ 'decoding' => 'async',
+ 'title' => wp_strip_all_tags( get_the_title() ),
+ 'alt' => '',
+ 'class' => "attachment-$size wp-post-image",
+ );
+
+ $image_attr = wp_parse_args( $attr, $default_attr );
+ $hwstring = image_hwstring( $width, $height );
+
+ $html = rtrim( " $value ) {
+ if ( $value ) {
+ $html .= " $name=" . '"' . esc_attr( $value ) . '"';
+ }
+ }
- $html = '';
+ $html .= ' />';
return trim( $html );
}