How is Wordpress' 'sizes' attribute, for images, computed? - css

Not sure if this started happening very recently, or if this has been an issue for longer, but I feel it's the former. I think I would have noticed, otherwise.
When I add an image to my post, and set the size to 'enlarged' or 'full' (presumably, I'm translating this from Portuguese), my (custom) theme shows these images either enlarged, or in full width. (To allow for this, I added add_theme_support('align-wide') to my functions.php.)
However, the file that is used, on larger screens, is not the correct one.
This is the image tag I get when including an image that is set to have full width:
<img loading="lazy" width="1003" height="564" src="[...]image.jpg?resize=640%2C360&ssl=1" alt="" class="wp-image-115865 shadow" srcset="[...]image.jpg?w=1920&ssl=1 1920w, [...]image.jpg?resize=300%2C169&ssl=1 300w, [...]image.jpg?resize=1024%2C576&ssl=1 1024w, [...]image.jpg?resize=1536%2C864&ssl=1 1536w, [...]image.jpg?resize=640%2C360&ssl=1 640w, [...]image.jpg?resize=1320%2C743&ssl=1 1320w, [...]image.jpg?resize=150%2C84&ssl=1 150w" sizes="(max-width: 640px) 100vw, 640px">
You can see that the image is displayed at 1003x564 pixels, but that the 'sizes' attribute sets a max-width at 640 pixels.
As a result, this image, displayed at 1003x564 pixels, is an upscaled image of 640 pixels wide.
The result is pixelated, which is not pretty.
What is defining the 'sizes' attribute? Why is it, here, set to 640 pixels? Can I change this? How?

The function that adds attr in images is wp_get_attachment_image()
The function that adds size attr in images is wp_calculate_image_sizes()
They have given filter wp_calculate_image_sizes to modify as per your need.

Related

wp_get_attachment_image: Best way to fill the 'sizes' attribute manually?

When using the wp_get_attachment_image() function in WordPress, by default it outputs a value in the 'sizes' attribute of the img tag. This sizes attribute tells the browser which thumbnail size of the image to load.
The value seems to be related to the largest image in the srcset attribute. So:
an image with dimensions of 2509x1673 gets: sizes="(max-width: 2509px) 100vw, 2509px"
an image with dimensions of 768x512 gets: sizes="(max-width: 768px) 100vw, 768px"
But this only works right when the image is being displayed over the full width of the browser window. If the image is shown in one column of a grid, the browser should be loading a much smaller version of the image.
So I overwrite the sizes attribute like this:
echo wp_get_attachment_image( $term_image, $term_image_size, false, array('sizes' => '(max-width: 575px) 258px, (max-width: 991px) 330px, 436px'));
But this feels like an absolute hassle. I understand that the server and/or WordPress are not able to decide what the sizes attribute needs to contain upon generating the DOM, because it doesn't take the CSS into consideration. But since WordPress also adds loading="lazy" to the markup, the image isn't being loaded directly in most cases. So maybe there are chances to change this on beforehand?
There should be a better way than manually filling the sizes attribute. Does anyone have any tips?

How does the browser pick the right image using <img> "sizes" and "srcset" attributes?

How does the srcset attribute determine the correct image in conjunction with the sizes attribute? Take this image for example:
<img alt="Demo image"
sizes="(min-width: 1024px) 512px,
100vw"
srcset="img/banner-large.jpg 2048w,
img/banner-medium.jpg 1400w,
img/banner-small.jpg 800w">
(I cleared the cache in Chrome)
I thought that the last image would always be chosen (800w), because of the 512px (sizes) and the image width of 800w (srcset), but that image did not always get selected? Why?
Here is a detailed guide on using Srcset with sizes.
sizes attribute contains a comma-separated list. Each item in the list describes the size of the image in relation to the viewport.
Using the sizes attribute with srcset provides the browser with enough information to start downloading the right image as soon as it sees an <img> tag in HTML without waiting for styles sheets to complete downloading and parsing.
Why do we need sizes?
If you are wondering why the browser is not aware of how big the image will render, checkout how the browser loads a web page.
The syntax:
<img src="image.jpg"
srcset="small.jpg 300w,
medium.jpg 600w,
large.jpg 900w"
sizes="(max-width: 300px) 100vw, (max-width: 600px) 50vw, (max-width: 900px) 33vw, 900px"
/>
Each comma-separated item in sizes has:
Media conditions, for example, (max-width: 300px) - It describes a possible state that the screen can be in. (max-width: 300px) means when the viewport width is 300 CSS pixels or less. It is similar to media queries but with some limitations. You cannot use screen or print.
An empty space.
The width of the image element when the media condition is true. You can provide an absolute length (px, em) or a length relative to the viewport (vw), but not percentages.
Demo - srcset with sizes
Let’s see this in action with a live demo - https://imagekitio.github.io/responsive-images-guide/srcset-sizes.html
The layout is such that:
If viewport width is above 900px, each image takes a fix 225px width.
Upto 900px, each image takes up 33vw i.e. 33% width of total viewport width.
Upto 700px, each image takes up 50vw i.e. 50% width of total viewport width.
Upto 400px, each image takes up 100vw i.e. the whole viewport width.
HTML markup of a single image element looks like this:
<img src="https://ik.imagekit.io/ikmedia/women-dress-1.jpg"
srcset="https://ik.imagekit.io/ikmedia/women-dress-1.jpg?tr=w-225 225w,
https://ik.imagekit.io/ikmedia/women-dress-1.jpg?tr=w-300 300w,
https://ik.imagekit.io/ikmedia/women-dress-1.jpg?tr=w-350 350w,
https://ik.imagekit.io/ikmedia/women-dress-1.jpg?tr=w-640 640w"
sizes="(max-width: 400px) 100vw, (max-width: 700px) 50vw, (max-width: 900px) 33vw, 225px">
Let’s see what happens at different screen size and DPR values -
The sizes attribute determines the media condition that applies to the current device width (the first media condition that applies is selected). Then, the browser checks the srcset attribute to find the image that most closely matches the slot size determined by the sizes attribute. Note that the browser chooses a larger image for high-res displays like Retina (usually chooses an image close to double the width it would choose on a "normal" resolution display). See Responsive images and Responsive images: If you're just changing resolutions, use srcset for a lot more detail on how this works.
So in your case, you would expect to get the banner-small.jpg image on a "normal" resolution device where device width is at least 1024px (and you would also expect to get that same image on smaller width "normal" resolution devices due to the larger widths of your other srcset image options).
Some reasons that you might not get the results you expect:
Older browser versions don't support srcset
Did not specify <meta name="viewport" content="width=device-width"> to force devices to adopt their real width when loading the page.
Browsers may display cached images rather than selecting the closest size match from srcset (as you noted in your question).
When attempting to display multiple sizes of the same image on a page, browsers may download only the largest image needed and use it in each instance (rather than downloading multiple images).
If you are attempting to test image selection behavior in some sort of sandboxed iframe environment like jsfiddle, it is difficult to be sure that the browser is interpreting the size of the display pane as "device width" (I have had mixed results with srcset in these types of environments). It seems to behave reasonably in a SO snippet for browsers like Chrome and Firefox (as long as you clear the cache). See below for an example snippet (better used in your own environment) that makes it visually obvious which image has been selected when testing.
<img srcset="https://via.placeholder.com/300x150 300w,
https://via.placeholder.com/600x300 600w"
sizes="(min-width: 1024px) 600px,
100vw"
src="https://via.placeholder.com/300x150"
alt="placeholder image">

Can I use `<img srcset>` or `<picture>` for image size rather than viewport size?

I'm trying to use <img srcset /> to load images based on the size they'll appear on the page, rather than based on the size of the viewport. Perhaps examples will help:
On any viewport, when the image is styled with width: 100% inside a 200px-wide <div>, img-200.jpg should load.
On any viewport, when the image is styled with width: 100% inside a 400px-wide <div>, img-400.jpg should load.
...etc.
I've done tons of Googling, but as best as I can tell, srcset is only used for changing images based on viewport size, not image size, if that makes sense. Is that correct? Or is there hope for what I'm trying to accomplish?
As was mentioned in the comments, this doesn't exist yet.
However, I've been thinking. If you don't mind a little trickery, there's a workaround.
We can use an iframe.
To the contents of an iframe, its width is the viewport. So then we can use the standard srcset tricks.
Let's say our img looks like this
<img src="https://placehold.it/200x100" alt=""
srcset="https://placehold.it/200x100 200w, https://placehold.it/500x250 500w">
using the 200x100 image at smaller resolutions (200px or less) and the 500x250 one at higher resolutions.
We can then put this in a HTML file with a zero margin around it, because the iframe expects an actual HTML document.
Now to avoid having to load multiple files, we can turn this into a data URL, which will look as follows:
data:text/html;charset=utf-8,%3Cbody style='margin%3A0'%3E%3Cimg src='https%3A//placehold.it/200x100' srcset='https%3A//placehold.it/200x100 200w, https%3A//placehold.it/500x250 500w
and all that will make our original HTML page look something like this.
Note that, to show it works, I included two iframes, which are identical in themselves. Only their CSS widths differ.
iframe {width:200px; height:100px}
iframe ~ iframe {width:400px; height:200px}
<iframe src="data:text/html;charset=utf-8,%3Cbody style='margin%3A0'%3E%3Cimg src='https%3A//placehold.it/200x100' srcset='https%3A//placehold.it/200x100 200w, https%3A//placehold.it/500x250 500w' alt=''%3E" frameborder="0">?</iframe>
<br><br>
<iframe src="data:text/html;charset=utf-8,%3Cbody style='margin%3A0'%3E%3Cimg src='https%3A//placehold.it/200x100' srcset='https%3A//placehold.it/200x100 200w, https%3A//placehold.it/500x250 500w' alt=''%3E" frameborder="0">?</iframe>

Why sizes attribute in img tag as we can use media query with image container instead?

OK this is the case : we want to display image in 50% of view port when viewport size is 800px or more otherwise image is 100% so the html look like the following:-
<div class="container">
<img src="image.jpg" srcset="image.jpg 500px" sizes="(min-width:800px) 50vw,100vw" />
</div>
OR we can simply do the following through css:-
.container img{
min-width:100%;
}
#media screen and (min-width:800px){
.container{width:50%}
}
So we can control image container instead of image itself, my question here is is there is a condition that sizes attribute is considered as the best solution ?
Notice:- I know that in the first example image size will be calculated based on the view port width and not the container width
If you really only have one image resource, then you can do this with CSS only. srcset+sizes is only necessary if you want to let the browser choose which image file to download from a set of multiple files with different sizes. In that case, basing on viewport with in sizes is the only option (because browsers start to download images before external CSS has been downloaded, so container size is unknown).

CSS targeting specific images

I am looking for a way to target a specific image with CSS, if this is even possible.
I am running a CMS where the users can upload images and put them on their pages. I would like a way to target images with specific dimensions.
So the question is, can you make a css line target a image or object with certain dimensional specifications ?
I would imagine something like :
img#width:400px { float :left; }
(to make images that are 400 pixels wide have float:left)
I know i could give the image a class or an id, but it would be lost when the customer plays around in the cms.
img[width="400"]{
float:left;
}
This will make any image that have the attribute set to 400 float left:
<img src="image.jpg" width="400" />
Note: this will only work if the width attribute is set. Will not work if the image is 400px wide but does not have a width attribute set.
According to the spec this should work
img[width=400]
but I haven't tried it

Resources