Skip to content

Instantly share code, notes, and snippets.

@phloe
Last active December 24, 2015 01:59
Show Gist options
  • Save phloe/6727092 to your computer and use it in GitHub Desktop.
Save phloe/6727092 to your computer and use it in GitHub Desktop.
Responsive image syntax through a "srcoptions" attribute

TL;DR

<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"/>
  • DRY - Expose url pattern and avaliable options for images - not full urls - in markup. Browser picks best options - builds url.
  • Responsive images need CSS to get layout. Browser preloading is a pre-RWD optimization. Preloading doesn't make sense with upcoming postpone and lazyload attributes anyway.
  • Client Hints can't be used exclusively. Has server requirement and needs alt. way of handling CH - anyway - when server req. can't be met.

Update 18/11/13: This gist coincidentally matches ideas voiced in this etherpad discussion (see #9 - [dpi/bandwidth]).



It seems like content and styling is being muddled together a little... We're drifting away from the separation of HTML and CSS that we've been working for for years.

Taking a couple of steps back and looking at what we've actually got:

  • Content: Image markup exposes data about the image source(s).
  • Design: CSS decides the actual displayed size of the image (CSS pixels).
  • Capability: The browser knows the pixel density, supported formats.

These three factors combined is what we have to use to make responsive images.

srcoptions

Instead of listing every available image url in the img-tag we could simply expose the available options (that are provided by prerendered images or serverside image service) through the srcoptions attribute.

The browser picks the best matching options and builds a url via the url-pattern.

The attribute's grammar goes a little like this:

<image-sizes> = [ <image-width> <image-height>? ]#

<src-options> = <url-pattern>, ? <image-sizes>, ? <device-pixel-ratios>, ? <file-formats> ? 

The browser picks a value from each of <image-sizes>, <device-pixel-ratios> and <file-formats> and fills in the tokens in <url-pattern> to get the complete url of the needed image.

If the value picked from <image-sizes> doesn't contain a height then {height} is extrapolated from the img-tag's width and height attributes (this is how you would do it in a polyfill):

    var width = 480, // picked from `<image-sizes>` by browser
        aWidth = img.getAttribute("width"), // 320
        aHeight = img.getAttribute("height"), // 240
        height = width * (aHeight / aWidth); // 360

There's no need to bring viewports and media queries into the equation; that stuff is already handled elsewhere in CSS!

Examples

An example with prerendered images:

<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"/>

The equivalent srcset version would be:

<img src="/path/to/foo-320w-240h-1x.jpg" width="320" height="240"
    srcset="/path/to/foo-320w-240h-1x.webp 320w 1x,
    /path/to/foo-480w-360h-1x.webp 480w 1x,
    /path/to/foo-640w-480h-1x.webp 640w 1x,
    /path/to/foo-320w-240h-1.33x.webp 320w 1.33x,
    /path/to/foo-480w-360h-1.33x.webp 480w 1.33x,
    /path/to/foo-640w-480h-1.33x.webp 640w 1.33x,
    /path/to/foo-320w-240h-2x.webp 320w 2x,
    /path/to/foo-480w-360h-2x.webp 480w 2x,
    /path/to/foo-640w-480h-2x.webp 640w 2x,
    /path/to/foo-320w-240h-1x.jpg 320w 1x,
    /path/to/foo-480w-360h-1x.jpg 480w 1x,
    /path/to/foo-640w-480h-1x.jpg 640w 1x,
    /path/to/foo-320w-240h-1.33x.jpg 320w 1.33x,
    /path/to/foo-480w-360h-1.33x.jpg 480w 1.33x,
    /path/to/foo-640w-480h-1.33x.jpg 640w 1.33x,
    /path/to/foo-320w-240h-2x.jpg 320w 2x,
    /path/to/foo-480w-360h-2x.jpg 480w 2x,
    /path/to/foo-640w-480h-2x.jpg 640w 2x" />

An example leveraging serverside imagescaling without Client hints; <image-size> and <device-pixel-ratio> are left out and raw window.devicePixelRatio, img.offsetWidth and img.offsetHeight used as {dpr}, {width} and {height} tokens:

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

The same example with Client hints; {dpr} and {format} tokens are left out and handled through CH-DPR and Accept headers (if no CH-DPR header is sent the server defaults to 1x gracefully):

<img src="/path/to/foo-320w-240h" width="320" height="240"
    srcoptions="/path/to/foo-{width}-{height}"/>

The <url-pattern> could even be optional if we standardized on a pattern (eg {path}-{width}-{height}-{dpr}.{format} where {path} could easily be extracted from the src attribute):

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

Which would give the shortest possible syntax for serverside imagescaling with Client hints enabled:

<img src="/path/to/foo-320w-240h" width="320" height="240" srcoptions/>

(Non-)Issues

Browser preloading

Was invented to make (mostly non-RWD) websites load images faster.

Herein lies the problem.

Most RWD sites use CSS to set width/height of img-elements - which means the preloader (as it runs even before CSS is loaded and applied) has no chance of knowing what size an img-lement will be displayed at in the end.

Also - preloaders may or may not have already have requested the src of an image by the time the responsive image url is resolved - resulting in 2 requests.

We need preloaders to skip img-tags with srcoptions attributes. Besides - preloader skipping is a feature that will have to be implemented anyways when the proposed postpone and lazyload attributes land.

Aggressive preloading and responsive images are not a good fit.

Client Hints

This would follow the seperation of concerns mantra - keeping browser and server capabilities a thing between the client and the server - with no need to expose them in the markup. Seems like a good idea! See Automating DPR switching with Client-Hints.

You could argue that this kind of black magic reduces transparency when one url will give different results on different devices due to client hints.

But - one thing is certain; CH can't stand alone. The server needs to be able receive and act on these hints. This may not be something you'll be able to control, eg. on shared webhosting. And the client needs to be able to send the hints. Which means responsive image syntax will need to have ways of exposing what the server supports (or what is available in the prerendered images) in addition.

Art direction

This should be a contract between the content provider and whatever renders the images. It doesn't belong in CSS or HTML.

@davegardner
Copy link

This is the only sane proposal I have heard so far. I have no idea whether browser-makers will go for it but content developers such as myself will love it.

@phloe
Copy link
Author

phloe commented Nov 25, 2013

Thanks ;)

There are a few extra factors I need to put in there like; progressive image formats and avoid default img.src requests (probably through ServiceWorker)...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment