Styling responsive layout in amp-img - html

Since I've implemented Google AMP I am struggling with this problem. Every time I add an image with a width far smaller than my website width, amp-img automatically add margins to keep the aspect ratio, like this:
I have tried other layouts mentioned in the [official documentation],(https://www.ampproject.org/docs/guides/responsive/control_layout#supported-values-for-the-layout-attribute) like flex-item.
With flex-item for example, I can get the desired behavior in the desktop version, that is, reducing the total margin of the image to look like this:
But in the mobile version, when an image is wider that the screen, the image overflows left and right.
Is there a way I can tweek the responsive layout in order to remove such larger margins when the image is relative small?
Investigating a bit in the code, the problem seems to be caused by the element i-amphtml-sizer, which is an element google-amp adds automatically and of which I have no control of.
I am not posting the url for my blog post in case it is considered spam, but if for some reason you need it, I'll update the question.
UPDATE
It seems more people are having this issue.

I solved it! reading amp documentation on github, in concrete the section on amp-html-layout, in "sizes" there is an example saying:
In the following example, if the viewport is wider than 320px, the image will be 320px wide, otherwise, it will be 100vw wide (100% of the viewport width).
<amp-img src="https://acme.org/image1.png"
width="400" height="300"
layout="responsive"
sizes="(min-width: 320px) 320px, 100vw">
</amp-img>
Previously, my image was:
<figure>
<amp-img
on="tap:lightbox1"
role="button"
tabindex="0"
layout="responsive" src="/img/securitynow.jpg"
width="100"
height="100">
</amp-img>
</figure>
After reading AMP documentation:
<figure>
<amp-img
sizes="(min-width: 100px) 100px, 100vw"
on="tap:lightbox1"
role="button"
tabindex="0"
layout="responsive" src="/img/securitynow.jpg"
width="100"
height="100">
</amp-img>
</figure>
Now it is displaying well on all screen sizes:
In order to work with all kind of image sizes, I'm following this rule:
sizes="(min-width: withOfImage) WithOfImage, 100vw"
This way, when the screen is wider than the image width, the image will have its original width, otherwise the image will be 100% of the viewport width.

Related

Responsive images, scrset in 25% of screen width, chooses to large image

So I have this code of a responsive image using scrset:
<picture class="background-image">
<source type="image/jpg"
srcset="http://localhost:61186/public/images/asia_1637411941_1920x1280.jpg 1920w,
http://localhost:61186/public/images/asia_1637411941_1720x1147.jpg 1720w,
http://localhost:61186/public/images/asia_1637411941_1520x1013.jpg 1520w,
http://localhost:61186/public/images/asia_1637411941_1320x880.jpg 1320w,
http://localhost:61186/public/images/asia_1637411941_1120x747.jpg 1120w,
http://localhost:61186/public/images/asia_1637411941_920x613.jpg 920w,
http://localhost:61186/public/images/asia_1637411941_720x480.jpg 720w,
http://localhost:61186/public/images/asia_1637411941_520x347.jpg 520w,
http://localhost:61186/public/images/asia_1637411941_320x213.jpg 320w">
<img src="http://localhost:61186/asia_1637411941_1920x1280.jpg" alt="Asia" />
</picture>
This (with surrounding html) renders to this on a 360px width screen:
Now, the image element is around 100px width. But Chrome loads the 1120px version (no cache / incognito tab)
Now ideally it would load the 320px width image as the element is just 100px width. But if that is not the case then based on the viewport of 360px I would at max expect it to pick the 520px version of the image. But it doesn't.
Now in Chrome Lighthouse I get a score penalty because I need to "properly size the images". But I feel like I'm doing that. Anyone has a solution?
If you run your markup through the W3 Validator you get
Error: When the srcset attribute has any image candidate string with a width descriptor, the sizes attribute must also be present.
I don't know the layout you're looking for and hence what media queries to use, but see if the following quick fix works for you:
<picture class="background-image">
<source type="image/jpg"
srcset="http://localhost:61186/public/images/asia_1637411941_1920x1280.jpg 1920w,
http://localhost:61186/public/images/asia_1637411941_1720x1147.jpg 1720w,
http://localhost:61186/public/images/asia_1637411941_1520x1013.jpg 1520w,
http://localhost:61186/public/images/asia_1637411941_1320x880.jpg 1320w,
http://localhost:61186/public/images/asia_1637411941_1120x747.jpg 1120w,
http://localhost:61186/public/images/asia_1637411941_920x613.jpg 920w,
http://localhost:61186/public/images/asia_1637411941_720x480.jpg 720w,
http://localhost:61186/public/images/asia_1637411941_520x347.jpg 520w,
http://localhost:61186/public/images/asia_1637411941_320x213.jpg 320w">
sizes="100vw">
<img src="http://localhost:61186/asia_1637411941_1920x1280.jpg" alt="Asia" />
</picture>

HTML picture element downloading wrong image on mobile

I'm running a page through Google's Page Speed Analyzer and for mobile, it's telling me I should download the proper size images. I know the viewport size, but I don't know the width of the slot because we are using a responsive layout. I've looked at the documentation and a bunch of examples and none of them are using what I think should be the correct image.
The Network tab of developer tools is showing we are downloading about.webp for mobile and not either of the smaller images (200px or 400px). Even when changing the device in Chrome to a smaller viewport width, it still downloads about.webp.
What is the correct HTML for a picture element to get Page Speed Analyzer to use the appropriate image?
Below is my picture element. Thanks!
<div class="row"><div class="col-md-3">
<picture>
<source type="image/webp"
sizes="(max-width: 200px) 200px,
(max-width: 400px) 400px,
(max-width: 1500px) 1500px"
srcset="//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w200.webp 200w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w400.webp 400w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt.webp 1500w">
<source type="image/png"
sizes="(max-width: 200px) 200px,
(max-width: 400px) 400px,
(max-width: 1500px) 1500px"
srcset="//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w200.png 200w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w400.png 400w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt.png 1500w">
<img src="//cdn.storyboardthat.com/site-images/articles/education/about-sbt.png"
alt="Storyboard That"
title="Storyboard That"
class="lazyload "
style="max-width:100%;height:auto;"
width="1500" height="400" loading="lazy">
</picture>
</div>
You have forgotten about Device Pixel Ratio (DPR).
You see the <picture> element basically says to browsers "here are a few options to choose from, I have indicated my preference but it is up to you to decide which image you think is best".
As you haven't specified a DPR preference the browser is using the following logic:
"Ok so I have 3 images to choose from, what is my current DPR? Oh I have DPR set to 3.
How big is the image at this screen width (320px)? Full screen width so I need a 320px image.
Ok so it is 320px * 3 DPR to ensure I have the highest quality image that matches my current display resolution.
So I need a 960px image minimum. My options are 200, 400 or 1500, I better choose the 1500 image and down sample it."
So how do I control pixel density decisions?
You can specify pixel density with 1x, 2x, 4x etc. For example:
<img src="default.webp"
srcset="hiddef.webp 2x, heighestdef.webp 4x"
alt="Image description">
However with how you have currently structured your picture element it won't quite work as you can't combine image 200w with image 2x to do image 200w 2x.
If you want to keep the same image at all DPIs then you would specify that image 3 times.
<img src="default.webp"
srcset="default.webp 2x, default.webp 4x"
alt="Image description">
Otherwise if you want the browser to change the image automatically based on DPR just leave your <picture> element as it is.
I see that the code snippet you have used is incorrect, refer this:
<picture>
<source media="(max-width:200px)" srcset="about-w200.webp">
<source media="(max-width:400px)" srcset="about-w400.webp">
<img src="about-sbt.webp alt="About" style="width:auto;">
</picture>
A working reference link

Setting dimensions of <img> fallback for <source> inside <picture>, for SEO/page loading time

Using the code below, I am trying to achieve this:
allow the browser to choose between WEBP <source> and JPEG in<img> (as fallback)
display the image 754px wide for viewport widths >1058px, or, for smaller viewports, select one of the five images automatically and stretch to viewport width.
have an src attribute of <img> in place, which points to a low-res lazyload version
specify width and height of image in HTML specified for SEO/UX reasons (more below).
<div>
<picture>
<source type="image/webp"
data-srcset="
image_100.webp 100w,
image_200.webp 200w,
image_400.webp 400w,
image_600.webp 600w,
image_754.webp 754w"
data-sizes="(min-width: 1058px) 1000px, 100%" />
<img data-srcset="
image_100.png 100w,
image_200.png 200w,
image_400.png 400w,
image_600.png 600w,
image_754.png 754w"
data-sizes="(min-width: 1058px) 1000px, 100%"
src="image_min.png" width="1000" height="300" alt="image_alt" />
</picture>
</div>
Why are there a data- prefixes, you ask? I want to use a lazyloading script, which will remove them, at the right time, when the image should be displayed.
Before the lazyloading script kicks in, the browser will ignore the <source> element, because proper srcset and sizes will be absent at that point. The browser will also ignore data-srcset and data-sizes of <img>, and will, therefore, display a low-res/placeholder image as defined in src.
Knowing the image dimensions and thus w/h ratio, I also want to supply these, in HTML, so that the browser knows what space to reserve for the image during loading time, before the images are loaded, to prevent re-rendering.
Simply setting absolute values of the image into style or into width and height attributes of the <img>, are only valid for viewport width >1058px. Otherwise, the dimensions are relative to viewport size. So, I want to tell the browser, that the image will have a width 100% and height 30% of width of parent <div>, which si valid in all cases.
I know that there is a css padding-top trick, to keep aspect ratio of a div, but I'm not sure I can use it in theis scenario, since it actually creates a padding above the image.
Any other ideas on setting the fallback image dimension?
Ok, so a plausible answer is, after all, wrapping the <picture> in a <div> container and applying CSS, including the padding-top hack, to maintain its' aspect ratio:
<div style="position:relative;"width:100%;padding-top:33%>
<picture>
<source type="image/webp"
srcset="
image_100.webp 100w,
image_200.webp 200w,
image_400.webp 400w,
image_600.webp 600w,
image_754.webp 754w"
sizes="(min-width: 1058px) 1000px, 100%" />
<img srcset="
image_100.png 100w,
image_200.png 200w,
image_400.png 400w,
image_600.png 600w,
image_754.png 754w"
sizes="(min-width: 1058px) 1000px, 100%"
src="image_min.png" style="width:100%;height:100%;position:absolute" alt="image_alt" />
</picture>
</div>
CSS of the fallback <img>: width and height set to 100%, and position:absolute
The <div> wrapper must be CSS position:relative, for the image to be positioned inside it "absolutely".
The <div> must have width and padding-top set to reflect the dimensions/aspect ratio of the image.
The CSS does not need to be defined inline, and so the size of the div wrapper can be set differently for different viewport sizes using media queries.
It works as follows:
The <img> element fills up the <div> wrapper completely. Thanks to position:absolute, the padding-top of the <div> wrapper has no effect on position of the image. The percentage value of padding-top makes it relative to the element width, so aspect ratio remains constant (e.g. padding-top set to 100% would make the wrapper always square).
The main benefit for SEO/UX:
No matter what the sizes and the srcset values are, if the dimensions (thus aspect ratio) are known, we can create a placeholder using CSS of the fallback <img>, and so reserving space for an image which has not yet been loaded, or even chosen by the browser from the srcset.
Ultimately this will prevent annoying changes in page layout, caused by images being painted on a page which has alreadey been rendered.

Prevent upscaling with scrset?

Is it possible to prevent upscaling when using scrset?
Here's a jsbin showing what I'm talking about:
https://jsbin.com/bukupuq
The browsers (Firefox, Chrome, Safari) are using the largest image to fill the container, even though its width (500px) is smaller. I would expect that it would intelligently use the best image given the viewport width, but not upscale the image.
Is there a way to prevent this without having to write an inline style="max-width:500px"?
It looks like images with srcset will always upscale, even if supplied with a low-res image. It's hard to search for documentation on this because all of the examples people use assume that you already have high-res images on hand. In my case, I'm building a 'responsive image' React component that is handling images supplied by a user from a CMS. Not all of their images are high-res, though.
My solution involves a little CSS balancing of width and max-width, both on the image itself and its wrapping <figure> tag. (This of course could be a div or anything else)
CSS:
img {
max-width: 100%;
}
figure {
width: 100%;
}
HTML:
This is generated according to how many sizes of an image are available. On the CMS side, I am creating thumbnails at 2400px, 1600px, 1200px, 800px, and 400px wide, but only if the source image is larger than that. The <figure> tag then gets all of the resized images, as well as the original one. Then, to prevent the image from scaling up, the figure tag gets a max-width of the largest image available.
So, if the source image is only 500px wide:
<figure class="" style="max-width: 500px;">
<img src="http://example.com/images/img_7804a_web.jpg"
srcset="http://example.com/images/img_7804a_web-400x600.jpg 400w,
http://example.com/images/img_7804a_web.jpg 500w"
sizes="100vw"
alt="alt text">
</figure>
If the source image is 1125px wide, it gets a few more sources:
<figure class="" style="max-width: 1125px;">
<img src="http://stephanie.standard-quality.biz/content/projects/18-2016/3-cargo-cults/cargocults_basketwoman_web.jpg"
srcset="http://stephanie.standard-quality.biz/thumbs/projects/2016/cargo-cults/cargocults_basketwoman_web-400x533.jpg 400w,
http://stephanie.standard-quality.biz/thumbs/projects/2016/cargo-cults/cargocults_basketwoman_web-800x1067.jpg 800w,
http://stephanie.standard-quality.biz/content/projects/18-2016/3-cargo-cults/cargocults_basketwoman_web.jpg 1125w"
sizes="100vw"
alt="alt text">
</figure>
Here's how it ends up looking: in this instance, the third image wasn't high-res enough to span the whole column:

Responsive images using srcset/sizes isn't respected by Safari iOS

I have the following
<img
src="/img/footer/logo_white.png?v=2.0"
srcset="/img/footer/logo_white.png?v=2.0 1x,
/img/footer/logo_white2x.png?v=2.0 2x"
>
which works fine on normal and hiDPI screens.
But when the viewport's very small (below 400px) the logo doesn't fit therefore I need a smaller version of the image in terms of real length, which I created. Then I tried
<img
class="biw-logo"
sizes="(max-width: 390px) 110px, 175px"
src="/img/footer/biw_logo.png?v=2.0"
srcset="/img/footer/biw_logo_small.png?v=2.0 110w,
/img/footer/biw_logo.png?v=2.0 175w,
/img/footer/biw_logo2x.png?v=2.0 350w"
>
Which works in terms of showing the _small image for viewports lower than 390 pixels - but now I've lost the "high resolution" factor; I cannot force the iOS browser in iphone5s to display a 220px image in length of 110px with the above syntax.
Could you correct my syntax?
<img class="biw-logo" sizes="(max-width: 390px) 110px, 175px" src="http://placehold.it/175x75" srcset="http://placehold.it/110x50 110w,
http://placehold.it/175x75 175w, http://placehold.it/350x150 350w">
You can do that with srcset and sizes. At first tell the browser which images you have available and how many pixels wide these images are, this can be done with srcset:
<img srcset="
/img/footer/logo_white.png?v=2.0 300w,
/img/footer/logo_white2x.png?v=2.0 600w,
/img/footer/logo_white_small.png?v=2.0 150w
">
Now the browser knows it can select from three images that are 150, 300 and 600 pixels wide (I guessed the dimensions, your actual widths might be different).
Second, you tell the browser how large the image will be displayed in the webpage, this can be achieved with sizes:
<img
sizes="(max-width: 400px) 150px, 300px"
srcset="..."
>
The browser knows now, that if the width of the viewport is 400px or less the image will be displayed 150px wide, for viewports larger than 400px it is displayed 300px wide.
This is enough information for the browser to select the right image. On a normal desktop with a normal screen it will select the 300w-image and on a HiDPI desktop it will be the 600w one. On a small viewport with a normal screen the 150w will get selected and on a small viewport with HiDPI the 300w one.
If you want more information about srcset and sizes, take a look at http://ericportis.com/posts/2014/srcset-sizes/.
You also can try using a couple more sources like that:
<picture>
<source srcset="img.png" media="(resolution: 150dpi)" type="image/png" />
<source srcset="img2x.png" media="(resolution: 300dpi)" type="image/png" />
<img src="img.png" alt="alt text" />
</picture>
Or something like that - i didn't test it, i need to learn more about resolution media query to be sure.