The answer is to use WP's responsive images, customising the sizes attribute to suit your use case.
Reading between the lines, I'm assuming here that you are using a responsive design approach to show the different layouts at different widths.
The easiest way is to start by making yourself a replacement template tag function for the_post_thumbnail
:
function wpse_221989_the_post_thumbnail( $size, $attr ) {
the_post_thumbnail( $size, $attr );
}
Where you call the_post_thumbnail
in your templates, call wpse_221989_the_post_thumbnail
instead. Why do we do it like this? WP doesn't give you any context on the filter hooks to modify srcset
and sizes
attributes for responsive images, so we are making our own context with the wpse_221989_the_post_thumbnail
wrapper function.
So far, nothing clever, but now we can build upon this function to:
1 - draw on a set of image sizes
2 - ask the browser to choose a suitable image depending upon your responsive breakpoint for this particular use case
1 - In your instance, where your image versions are all the same aspect ratio, WP handles things for us. Using a built-in image call like the_post_thumbnail
will build a srcset
attribute from every image size you have with the same aspect ratio as the image specified in the $size
parameter. You can use add_image_size
to create additional versions large enough for high density displays too. WP's match on aspect ratio allows for small rounding errors, so both the sizes in your question should be matched OK.
2 - To get the browser to choose the right image size for the space, you will need a custom sizes
attribute. WP's default sizes
attribute assumes that you want an image to be full browser width, but no wider than the image's natural width. In your design, narrower screens need the larger image and then above a certain breakpoint you want to choose the smaller image width.
So, assuming from your image sizes that your breakpoint is at 640px
, we'd want a sizes
attribute a bit like this: (min-width: 640px) 33.333vw, 100vw
What this says to the browser is for windows of 640px
width and over, choose the image from the srcset
list that best matches a third of the window width. For other window widths (< 640px) choose an image from the list that best fits the full window width.
To put that into code we make a new function to return the attribute and build it into our wrapper function:
function wpse_221989_sizes() {
return "(min-width: 640px) 33.333vw, 100vw";
}
function wpse_221989_the_post_thumbnail( $size, $attr ) {
add_filter( 'wp_calculate_image_sizes', 'wpse_221989_sizes', 10 , 2 );
the_post_thumbnail( $size, $attr );
remove_filter( 'wp_calculate_image_sizes', 'wpse_221989_sizes', 10 );
}
Now when your theme template calls wpse_221989_the_post_thumbnail
our custom sizes
attribute will be substituted for the default. By hooking into the filter before calling the_post_thumbnail
and unhooking immediately after we make our custom template tag function sensitive to the context of your question without upsetting any sizes attributes generated on images in other parts of your theme.
You can build upon this by putting in conditional checks on $size
to choose different rules for the various image versions you've created for your theme to use.
If Google Pagespeed is important to you then test to see that it is obeying your srcset
and sizes
attributes. If not, then use add_image_size
to make a size that just fits the Pagespeed window and use that size in your calls to wpse_221989_the_post_thumbnail
so that it is put into the src
attribute. On my sites I use a 1024px image as the default which seems to keep both desktop and mobile Pagespeed analyses happy.