Understanding srcset and sizes in combination with HiDPI monitors
Asked Answered
A

5

6

I have been into CSS for quite a while now, but srcset and sizes for the image element confuse me. Here is an example that I thought would work.

<img alt="Background image flowers"
    srcset="img/flowers-480.jpg 480w,
            img/[email protected] 480w 2x,
            img/flowers-768.jpg 768w,
            img/[email protected] 768w 2x,
            img/flowers-960.jpg 960w,
            img/[email protected] 960w 2x,
            img/flowers-1280.jpg 1280w,
            img/[email protected] 1280w 2x" 
    sizes="(max-width: 1279px) 100vw,
           (min-width: 1280) 1280px"
    src="img/flowers-960.jpg">

The idea is to have an image that's 100% of the viewport until the viewport is 1280px wide or wider, then the image will be fixed size. However, to compensate for higher DPI devices I thought it was recommended to add DPI descriptors (1.5x, 2x and so on), as suggested here and here.

What I thought the above code would do is:

  • check sizes, see what kind of size to expect for the image (and if a relative unit is given such as % or vw, calculate the pixel width)
  • find the images in srcset width that is closest to that width
  • from these images, filter out the one with a DPI descriptor closest to the device's DPI

However, when I put this through a validator I get the following error:

Error: Bad value for attribute srcset on element img: Width for image img/[email protected] is identical to width for image img/flowers-480.jpg

So clearly I am completely missing the point of how srcset and sizes work. What am I doing wrong?

Arborization answered 8/3, 2018 at 13:37 Comment(0)
E
4

As defined on MDN for <img srcset="...">:

Each string is composed of:

a URL to an image, optionally, whitespace followed by one of:

  • a width descriptor, or a positive integer directly followed by 'w'. The width descriptor is divided by the source size given in the sizes attribute to calculate the effective pixel density.
  • a pixel density descriptor, which is a positive floating point number directly followed by 'x'.

You tried to use both, and that's illegal.

Enzymolysis answered 10/3, 2018 at 21:5 Comment(0)
C
7

The "art direction use case", linked in @vbarinov's answer, quoted from developers.google.com, shows how to combine both width and pixel density as criteria for which image source to load and display:

<picture>
  <source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x">
  <source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x">
  <img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" alt="a head carved out of wood">
</picture>

In viewports that are at least 800 pixels wide, head.jpg should be displayed on a single density display, while head-2x.jpg should be displayed on a display with double or greater density.

Viewports below 800 pixels with, but at least 450 pixels wide, single-density displays should show head-small.jpg, while head-small-2x.jpg is for double density or greater.

The fallback image definition inside the <img> tag has another srcset that defines a double-density version (head-fb-2x.jpg) for viewports below 450 pixels width.

Single-density devices and browsers that don't support source sets or picture elements will fall back to display head-fb.jpg.

In a more complex scenario, we can also add alternative image formats, to take advantage of webp compression while providing jpg versions for browsers without webp support.

<picture>
  <source media="(min-width: 800px)" srcset="head.webp, head-2x.webp 2x" with="800" height="941">
  <source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x" with="800" height="941">
  <source media="(min-width: 450px)" srcset="head-small.webp, head-small-2x.webp 2x" width="800" height="941">
  <source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x" width="800" height="941">
  <img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" width="400" height="471" alt="a head carved out of wood">
</picture>

Here is a codepen of the complex example.

Cusack answered 23/2, 2022 at 18:10 Comment(2)
Nice! Does this mean we can duplicate the source element for a given media query in order to serve different image formats? (AVIF when it's supported and jpeg as a fallback?)Psaltery
@Psaltery should be possible. I built a codepen codepen.io/openmindculture/pen/mdpbRZK with webP (you could use AVIF alternatively). Dev tools allow to disable webP/AVIF support in the "Rendering" (formerly "Accessibility" tab.Cusack
E
4

As defined on MDN for <img srcset="...">:

Each string is composed of:

a URL to an image, optionally, whitespace followed by one of:

  • a width descriptor, or a positive integer directly followed by 'w'. The width descriptor is divided by the source size given in the sizes attribute to calculate the effective pixel density.
  • a pixel density descriptor, which is a positive floating point number directly followed by 'x'.

You tried to use both, and that's illegal.

Enzymolysis answered 10/3, 2018 at 21:5 Comment(0)
L
2

According to MDN, "It is incorrect to mix width descriptors and pixel density descriptors in the same srcset attribute. Duplicate descriptors (for instance, two sources in the same srcset which are both described with '2x') are invalid, too."

You have 2x listed 4 times. That's invalid.

Here is an example from MDN:

Example 4: Using the srcset and sizes attributes

The src attribute is ignored in user agents that support srcset when using 'w' descriptors. When the (max-width: 600px) media condition matches, the image will be 200px wide, otherwise it will be 50vw wide (50% of the viewport width).

<img src="clock-demo-thumb-200.png" 
    alt="Clock" 
    srcset="clock-demo-thumb-200.png 200w,
    clock-demo-thumb-400.png 400w"
    sizes="(max-width: 600px) 200px, 50vw">
Lenee answered 12/3, 2018 at 19:19 Comment(0)
I
2

For a combination of HPDI settings and responsive sizes for images you should actually use <picture> with a few <source> and a fallback <img> elements.

Details are in this article.

Immature answered 7/9, 2021 at 14:39 Comment(1)
I would like to add the example code at least, but the edit queue is full.Cusack
L
1

As of 2022, I'd argue it's just easier to assume high DPI screens and leverage the breakpoint idea of srcset:

<img alt="Background image flowers"
  srcset="img/flowers-480.jpg 480w,
          img/flowers-768.jpg 768w,
          img/flowers-960.jpg 960w,
          img/flowers-1280.jpg 1280w,
          img/flowers-1920.jpg 1920w, 
          img/flowers-4096.jpg 4096w"
   sizes="(max-width: 1279px) 100vw,
          (min-width: 1280) 1280px"
     src="img/flowers-960.jpg">
Lusty answered 23/2, 2022 at 18:36 Comment(6)
Your example only covers small high dpi screens (like typical smart phones). For 4k desktops, you would need larger images like: <img alt="Background image flowers" srcset="img/flowers-480.jpg 480w, img/flowers-768.jpg 768w, img/flowers-960.jpg 960w, img/flowers-1280.jpg 1280w", img/flowers-1920.jpg 1920w", img/flowers-4096.jpg 4096", sizes="(max-width: 1279px) 100vw, (min-width: 1280) 1280px" src="img/flowers-960.jpg">Illbred
Sure, it was just a response to the OP's question. Updated to include your edits.Lusty
I assume high DPI screens too, however I can't find any stats on that. Do you have a source? There are plenty of stats on resolution, not on dpi.Foreland
@KasperKamperman - I think all mobile devices have been high DPI for at least 9-10 years now. There are still HD monitors for the desktop audience though, so you might want to consider that.Lusty
I'm running a test right now on my own website by tracking window.devicePixelRatio. Indeed 50% of mobile is already 3x and otherwise 2x. Desktop seems 75% 1x and other part 2x.Foreland
Functionally it doesn't really matter though - just serve high DPI at this point since it's the majority of traffic.Lusty

© 2022 - 2024 — McMap. All rights reserved.