QR Code contains TinyURL of this article.Responsive Images Without Browser Reflow

this website viewed on a desktop computer, laptop, tablet and cellular phone

tldr; calc((image_height/image_width)*100%) — it’s a kinda magic.


Wherever possible, web-designers should avoid operations that result in browser reflow (or re-paint). These are expensive in terms of performance and can result in a jarring experience for the user.

So what do we mean when we talk about browser reflow? We refer to any operation that results in the browser having to repaint the page. Examples include: scripts that append/delete/change elements in the DOM; resources loaded from third-parties; font replacement…1

But we don’t need to stretch to such exotica. An operation as simple as loading an embedded image can result in a reflow and it can be an irritating experience.

Let’s consider an example:

web page before reflow operation
fig. 1
web page after reflow operation
fig. 2
fig. 1
This is a snapshot of a web-page as it is loading. Notice the spacing between the title and the bold text below it. Now imagine you’re about to click a link on that page. You position your mouse-pointer over the link then, just as you click, the image completes loading (fig. 2). The web-browser registers your click on the image rather than the link you had targeted. Furthermore, the layout of the whole page has now changed. The textual content has moved down the page to accommodate the image… and that’s kind of frustrating, particularly if it’s moved “below the fold.”
<img src="/images/kitten.jpg" width="640" height="426" />

Now back in the pre-enlightenment days, before we got all responsive with our designs, the solution for this was rather simple. We’d simply declare the dimensions of our images in our markup (example above) and the browser would use that information to reserve space accordingly, then populate those spaces with the corresponding images once they finished loading:


web page before image has loaded, with space reserved for the image
fig. 3
web page after the image has loaded
fig. 4
fig. 3
In this snapshot we can see the space reserved for an image by the width and height attributes in the image markup (I’ve added in the red border for illustrative purposes). When the image completes loading (fig. 4) there is no change in the layout as the image simply occupies the space already allocated for it. This makes for a better overall user experience.

Unfortunately, width and height attributes are meaningless in the context of responsive design. We don’t know in advance what dimensions the web-browser will render our images with. Those values are almost infinitely variable as they depend on screen size and orientation, pixel density and browser dimensions. So what can a quality-conscious web-designer do?

The good news is, there is a solution.


We know the dimensions of our images. The web-browser knows everything about the environment that it will render them in. We simply need to combine our knowledge.

Consider the following magical CSS incantation:

calc((image_height/image_width)*100%)

With the attributes we know — the image’s width and height — and the container width that the browser knows, we can have the web-browser calculate a height derived from the ratio of our image dimensions.2

For example, with image dimensions — 640×426 — the result is:

(426 / 640) × 100% = 66.5625%

Now, if our image is within a container that renders at 640 pixels wide, then 100% ≡ 640. Therefore, with the result of the maths above, we can perform a new equation:

640 × (66.5625 / 100) = 426

which is a mathematical proof that our derived height is correct.


So, let’s put this to the test and create a place-holder:

<div style="max-width: 640px;"><!-- container -->
  <figure style="padding-bottom: calc((426/640)*100%);">
  </figure>
</div>

“Oh no,” I hear you cry, “a container div! That’s not semantically correct. Is it really necessary?”

Good question. The answer is “no!” If your image is going to render at 100vw (or 100%) width. But I’ve deliberately chosen to display the image at a slightly smaller width in these examples, to illustrate how you’d handle that… so stop interrupting. 😃

Back to the example then, the above code results in:3

which gives us exactly the vertical space we need to accommodate our image. So, if we put our image inside that figure element it should be all good:4

<div style="max-width: 640px;">
  <figure style="padding-bottom: calc((426/640)*100%);">
    <img src="/images/kitten.jpg" />
  </figure>
</div>
cute kitten

Oh dear. That’s not what we had in mind is it?

So what’s happened here? Well, we gave the figure a border-bottom value rather than a height value. We did this because, had we used height, the figure wouldn’t occupy any vertical space on screen until the image within it had completed loading. Which is the undesirable behaviour we are actually trying to address. When we set a padding-bottom value, the browser will render that padding (i.e. vertical space), no matter what.

The problem is that it then renders the image above that space, as you can see above.

Fortunately, we are just key-strokes away from achieving our goal… with a little help from CSS positioning:

<div style="max-width: 640px;">
  <figure style="padding-bottom: calc((426/640)*100%); position: relative;">
    <img src="/images/kitten.jpg" style="position: absolute;" />
  </figure>
</div>
cute kitten

Tada!

Go ahead, empty your cache and refresh. Notice the absence of a reflow? Boom… we nailed it.

Browser support for calc is almost total, so you can implement this right now.

This technique5 has allowed me to address one of the biggest issues I had with responsive web-design. I hope that you put it to use in your websites and make the web a little bit better for us all.

  1. How Browsers Work ↩︎

  2. Since I wrote this article I have learned that we call this technique “Intrinsic Placeholders.” ↩︎

  3. Note that the markup shown is a simplified version of the underlying code. I’ve removed everything that’s not essential to explaining the concept. ↩︎

  4. Cute kitten photograph courtesy of Nicolas Suzor↩︎

  5. NOTE: I did not invent this technique. I don’t know who did. While researching the problem I found countless articles describing this solution. Responsive image reflow has been a pain point for me for some time and I wanted to document the resolution in my own words. ↩︎