Tab Completion

I'm Tab Atkins Jr, and I wear many hats. I work for Google on the Chrome browser as a Web Standards Hacker. I'm also a member of the CSS Working Group, and am either a member or contributor to several other working groups in the W3C. You can contact me here.
Listing of All Posts

Explaining The Last Clause of the src-n Grammar

Last updated:

Since I've been shopping the src-n proposal for responsive images around, people have had some OPINIONS.

The first two parts are pretty simple: we got Media Queries, and we got "simple" srcset (a list of image urls and pixel densities). Everyone's behind that stuff, it's easy, no problem.

What people have been complaining about is the last part of the grammar, described in the Variable-Sized Images section.

People who haven't studied the use-cases gathered by the RespImg folks don't understand what this branch of the syntax does, and decry its complexity. It's really quite easy to defend, though. It's a compact way of expressing all the information you need to handle multi-resolution images of variable size. The image might vary based on the viewport width (for example, it might fill 100% of the viewport), or it might vary based on your layout breakpoints, as you deliver differently styled pages to small and large screens.

The spec itself does a good job at defending its usefulness in the simplest case, when your image is a percentage of the viewport. That looks something like this:

<img src-1="100%; 400.jpg 400, 800.jpg 800, 1600.jpg 1600">

This isn't too hard to read - the first part, before the semicolon, says that the image's width will be 100% of the viewport's width. The second part is just a list of images, but rather than giving each a density descriptor, it just provides their width in image pixels.

The browser uses these two pieces of information to figure out the effective density of each source image, and chooses which to download based on that. For example, on an iPhone with a 320px viewport, the first source is effectively 1.25x, while the second is 2.5x. On the other hand, on a 1000px-wide laptop screen, the second source is .8x and the third is 1.6x.

Doing the same thing with srcset is possible, but it's verbose, especially if you want to future-proof things well to cover densities from .5x (potentially useful for downloading things quickly over a slow mobile connection) up to 3x (some devices on the market now are hitting this point now) and higher. The spec has an example of this.

More Complex Examples

What the spec doesn't have, because I didn't want to spend the time writing it, is an example of just how verbose srcset gets when you have a more complex example. Here's a pretty basic one:

<img src-1="100% (640px) 50% (960px) 33%; 
            160.jpg 160, 320.jpg 320, 480.jpg 480, 640.jpg 640, 
            960.jpg 960, 1280.jpg 1280, 1920.jpg 1920">

Here, the first part is a bit more complex. It says that your layout has two breakpoints, at 640px and 960px, where your layout changes. In the smallest layout, this image will be presented at 100% of the viewport width, in the next it'll be 50% (a two-column design, perhaps), and in the largest it'll be 33% (a three-column design).

Reading this requires a little bit of knowledge, but nothing crazy. You need to know how the syntax works, but it's easy, and once you do, it's really simple to see what's going on. Importantly, it's also really easy to maintain - if your breakpoints change, or you add more, the necessary modifications to this syntax are small and obvious.

Here's the same thing expressed using srcset:

<img srcset="
  320.jpg .89x 400w, 480.jpg 1.33x 400w, 640.jpg 1.78x 400w,
  480.jpg 1.04x 520w, 640.jpg 1.39x 520w, 960.jpg 2.09x 520w,
  640.jpg 1.1x 639w, 960.jpg 1.66x 639w, 1280 2.2x 639w,
  320.jpg 0.89x 800w, 480.jpg 1.33x 800w, 640.jpg 1.78x 800w,
  480.jpg 1.09x 959w, 640.jpg 1.45x 959w, 960.jpg 2.18x 959w,
  320.jpg 0.89x 1200w, 480.jpg 1.33x 1200w, 640.jpg 1.78x 1200w,
  480.jpg 1.09x 1440w, 640.jpg 1.45x 1440w, 960.jpg 2.18x 1440w,
  480.jpg 0.86x 1920w, 640.jpg 1.14x 1920w, 960.jpg 1.71x 1920w, 1280 2.29x 1920w,
  640.jpg 0.86x, 960.jpg 1.29x, 1280 1.71x, 1920 2.57x

To put it lightly, this is fucking terrible.

  1. The breakpoints are completely invisible now. (If you look closely, you'll notice the odd width descriptors of 639w and 959w, which is a hint that something different's going on there, but that's an artifact of how they're supposed to match up with the MQs. If they'd been done as 640w and 960w, you wouldn't be able to see the breakpoints at all except by examining the relationship between each segment's max width and the densities of the images.)
  2. You have to do non-trivial math to figure out how many divisions to put in each breakpoint (they're split into 3, 2, and 4 sections each).
  3. You have to do non-trivial math to figure out the densities at each division - find the average of the min and max viewport widths for the section, then divide the image widths by that value.
  4. The knowledge that the image is 100%, 50%, or 33% of the viewport is completely hidden, and can only be recovered by reversing the density back to a width and comparing it to the viewport width limit.
  5. It's much less future-proof. This only covers approximately the 1x to 2x range. Smaller and larger densities aren't addressed at all, and doing so makes the markup even more terrible.
  6. It's so much less maintainable. Changing any segment limit involves fixing 4-5 NNNw numbers, and adjusting 4-5 densities. changing your layout to use a different image width involves recalculating 10-20 densities, since those are derived quantities.

Literally every single aspect of this markup is markedly worse than the src-n alternative. The pain is so great that I don't think it's possible to reasonably argue that forcing authors to do this is better than forcing authors to learn a new microsyntax for src-n.

One could try to argue that this use-case isn't valuable enough to address, but I think the use-case gathering by RespImg folks, especially John Mellor for this particular topic, puts the lie to that. This kind of thing is a reasonable thing for authors to do, and which will be done a decent fraction of the time when responsive images are used at all.

(a limited set of Markdown is supported)

a couple of extra resolutions, and an SVG path would have been probably smaller. This way to deal with resolutions for raster images is simply insane, I wish all browsers where already supporting SVG images, SVG icons for the home screen, SVG everything so the problem, at least for images that are not pictures, would be solved once for all.


(a limited set of Markdown is supported)

Re #2: SVG isn't helpful for photos, which represent most of the images on the web.

For images where SVG works, yeah, it's of course way better.


(a limited set of Markdown is supported)

I guess the only issue with these approaches is that information about the layout of your page is leaking through into the markup -- so if one were to completely redesign a website that was using this syntax, it would be necessary to update the markup to match. That being said, of course, the line between presentation and markup is distinctly blurry, particularly for the art direction use case...

I do like putting the onus on deciding which image to load onto the browser (using src-n) -- as you've suggested, this means that browsers can elect to load (what they assume to be) a smaller image in certain situations. I think that this is probably one of the big wins of the src-n approach, to be honest.


(a limited set of Markdown is supported)

The way I explained the complex part of the src-n syntax during the Fronteers conf was using a "spray and pray" analogy: You "spray" a whole bunch of image sources for a particular break-point, and "pray" that the browser picks the right one :)

I think people got it.


(a limited set of Markdown is supported)

Re #4:

I guess the only issue with these approaches is that information about the layout of your page is leaking through into the markup

Yeah, that's an unfortunate but necessary aspect of this. We're extremely restricted in what we can do here, since we have to be friendly to the preload scanner, which knows very little and can't depend on any linked documents.


(a limited set of Markdown is supported)

I agree that the src-n notation is wholy and clearly clearer than srcset. That said I feel like src-n could be clearer. At least, I had to read it 3-4 times.

In your example, the first statement is presentational logic, could we perhaps leverage CSS for that?

``` 100% (640px) 50% (960px) 33%; 160.jpg 160, 320.jpg 320, 480.jpg 480, 640.jpg 640, ```


(a limited set of Markdown is supported)

Re #8: CSS for individual images is difficult but I agree it shouldn't be in the markup either. Something like the favicon format shows how this should be done: in the image itself. Add filters to the http servers and browsers always get what they want. Simply asking for the size doesn't cut it if you want to reduce bandwidth.


(a limited set of Markdown is supported)

  • Could the size-viewport-list be specified as optional. In this case the browser might delay loading until the needed image pixel size were known, or speculatively load the smallest and reload later if necessary etc.
  • Could the intrinsic size be well defined, irrespective of the size-viewport-list.

(a limited set of Markdown is supported)

  • Would it be a useful test of the spec to ask if the size-viewport-list can be automatically generated given the html and css?

(a limited set of Markdown is supported)

#12 - Rasmus Fl√łe:

Re #4: I totally agree with you on the leaking part... If you insist that preloading makes sense in RWD - then leaking is a necessary evil :(

But I think that seperation of concerns should have more weight - especially considering the upcoming postpone and lazyload attributes.

src-n may have shorter syntax than srcset - but we could do it even shorter (let's call it srcoptions):

```html <img src="/path/to/foo-320w-240h-1x.jpg" width="320" height="240" srcoptions="/path/to/foo-{width}-{height}-{dpr}.{format}, 320w 480w 640w, 1x 1.33x 2x, webp jpg"/> ```

It's almost self explanatory :D but essentially exposes supported options for the browsers to pick and choose between and build the resulting url through a url pattern.

I wrote up a bit on it:


(a limited set of Markdown is supported)