responsive images – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Thu, 25 Aug 2022 21:02:44 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 responsive images – CSS-Tricks https://css-tricks.com 32 32 45537868 Barebones CSS for Fluid Images https://css-tricks.com/barebones-css-for-fluid-images/ Thu, 18 Feb 2021 16:25:55 +0000 https://css-tricks.com/?p=334668 Zach takes a look at some fundamental HTML+CSS usage for fluid, responsive images. Most of it, I’d say, is what you’d expect, but things get weird when srcset gets involved.

I poked my way through, and in addition to the …


Barebones CSS for Fluid Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Zach takes a look at some fundamental HTML+CSS usage for fluid, responsive images. Most of it, I’d say, is what you’d expect, but things get weird when srcset gets involved.

I poked my way through, and in addition to the weird thing Zach noted, wanted to add one more thing. Let’s start like this:

<img src="./img.jpg" alt="" />

With no other CSS involved, this renders at the “intrinsic size” of the image. Say the original image is 400px wide, it renders 400px wide.

We should be putting width and height attributes on images, because it allows the browser to make space for them even before they are downloaded (even when they are fluid, which is super cool). So:

<img src="./img.jpg" alt="" width="400" height="300" />

Also nothing terribly weird there. Even if we slap max-width: 100% in the CSS, that’ll do what we want: preserving space, behave fluidly, and not growing bigger than it should.

But let’s hold off on the max-width: 100% thing for a second. If we just use srcset and set up multiple sources.

<img src="./img.jpg" alt=""
     srcset="./img-200.jpg 200w, ./img-400.jpg 400w" />

BAM, we blow out the width of the thing.

That won’t render at 200px or 400px—it’ll actually render at 100vw, believe it or not. I think that’s because that’s the default sizes value. I normally think of the sizes attribute as not information about anything to do with actual layout, but just information for the browser to choose a source. But that’s not true here. It really does effect layout (in all browsers I tested). Here’s proof:

See the little one below it where all I change is the sizes.

Anyway that’s not what Zach honed in on, but it’s similar. Let’s put back the responsible thing and add in width and height attributes.

<img src="./img.jpg" alt="" width="200" height="137"
     srcset="./img-200.jpg 200w, ./img-400.jpg 200w" />

No more blowout (with or without sizes) but now we have a new weird problem. This is basically like saying max-width: 200px. Even though we have sources that are wider than 200px, we’ve capped the width at 200px. Zach puts it like:

Using max-width: 100% constrains the image to the container, but be careful when you use this with srcset—it may cap smaller than you want when using [width]! Pair with width: auto to fix this.

Zach’s final snippet is this, which I think reigns in all the weirdness:

img {
  max-width: 100%;
}
img[width] {
  width: auto; /* Defer to max-width */
}
img[width][height] {
  height: auto; /* Preserve aspect ratio */
}

/* Let SVG scale without boundaries */
img[src$=".svg"] {
  width: 100%;
  height: auto;
  max-width: none;
}

To Shared LinkPermalink on CSS-Tricks


Barebones CSS for Fluid Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
334668
Fluid Images in a Variable Proportion Layout https://css-tricks.com/fluid-images-in-a-variable-proportion-layout/ https://css-tricks.com/fluid-images-in-a-variable-proportion-layout/#comments Fri, 03 Jul 2020 14:36:32 +0000 https://css-tricks.com/?p=313543 Creating fluid images when they stand alone in a layout is easy enough nowadays. However, with more sophisticated interfaces we often have to place images inside responsive elements, like this card:

For now, let’s say this image is not semantic …


Fluid Images in a Variable Proportion Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Creating fluid images when they stand alone in a layout is easy enough nowadays. However, with more sophisticated interfaces we often have to place images inside responsive elements, like this card:

Screenshot. A horizontal card element with an image of two strawberries against a light blue background to the left of text that contains a heading and two sentences.

For now, let’s say this image is not semantic content, but only decoration. That’s a good use for background-image. And because in this context the image contains an object, we can’t allow any parts to be cropped out when it’s responsive, so we’d pick background-size: contain.

Here’s where it starts to get tricky: on mobile devices, this card shifts direction and becomes vertical, with the image on top. We can make that happen with any sort of CSS layout technique, and probably best handled with CSS grid or flexbox.

Screenshot. The same strawberry card but in a vertical format.

But as we test for smaller screens, because of the contain property, this is what we get:

The same card element in vertical but now the strawberry image is not flush with the top border of the card.
Hey, get back up there!

That’s not very nice. The image resizes to maintain its aspect ratio without cutting off any details, and if the image is important content and should not be cropped, we can’t change background-size to cover.

At this point, our next attempt might be familiar to you: placing the image inline, instead the background. 

On desktop, this works fine:

It’s not bad on mobile either:

But on smaller screens, because of all the fixed sizes, the image’s proportions get distorted.

Screenshot. The vertical card element with the strawberry image out of proportion, causing the strawberries to appear stretched vertically.
Hmm, those strawberries are not as appetizing when stretched.

We could spend hours fiddling with the image, the card, the flex properties, going back and forth. Or, we could…

Separate main content from the background

This is the base for obtaining much more flexibility and resilience when it comes to responsive images. It might not be possible 100% of the time but, in many cases, it can be achieved with a little effort on the design side of things, especially if this approach is planned beforehand.

For our next iteration, we’re placing our strawberries image on a transparent background and setting what was the blue color in the raster image with CSS instead. Go ahead and play with viewport sizes in this demo by adjusting the size of the sample space!

Looking deeper at the styles, notice that we’ve also added padding to the div that holds the image, so the strawberries don’t come too close to the edges. We have full control of how close or distant we want them to be, through this padding.

Note how we’re also using negative margins to compensate for the padding on our outer card wrapper, otherwise we’d get white space all around the image.

Use the object-fit property for inline images

As much as the previous demo works, we can still improve the approach. Up to now, we’ve assumed that the image was un-semantical content — but with this layout, it’s also likely that the image illustration could be more than decoration.

If that’s the case, we definitely don’t want the image to get cut off because that would essentially amount to data loss. It’s semantically better to put the image inline instead of a background to prevent that, and we can use the object-fit property to make it happen.

We’ve extracted the strawberries from the background and it’s now an inline <img> element, but we kept the background color in that same image div. 

Finally, combining the object-fit: contain with a 100% width makes it possible to resize the window and keep the aspect ratio of the strawberries. The caveat of this approach, however, is that we need to set a fixed height for the image on the desktop version — otherwise it’s going to follow the proportion of the set width (and reducing it will alter the layout). That might make things too constrained if we need to generate these cards with a variable amount of text that breaks into several lines.

Coming soon: aspect-ratio

The solution for the concern above might be just around the corner with the upcoming aspect-ratio property. This will enable setting a fixed ratio for an element, like this:

.el {
  aspect-ratio: 16 / 9;
}

This means we’ll be able to eliminate fixed height and replace it with our calculated aspect ratio. For example, the dimensions in the desktop breakpoint of our last example looked like this:

.image {
  /* ... */
  height: 184px;
  width: 318px;
}

With aspect-ratio, we could remove the height declaration and do the math to get the closest ratio that amounts to 184:

.image {
  /* ... */
  width: 318px; /*  Base width */
  height: unset; /* Resets the height that was set outside the media query */
  aspect-ratio: 159 / 92; /* Amounts close to a 184px height */
}

The upcoming property is better explored in this article, if you want to learn more about it.

In the end, there are multiple ways to achieve reliably responsive images in a variable proportion layout. However, the trick to make this job easier — and better — does not necessarily lie with CSS; it can be as simple as adapting your images, whether that’s by separating the foreground from background (like we did) or selecting specific images that will still work if a fair portion of the edges get cropped.


Fluid Images in a Variable Proportion Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/fluid-images-in-a-variable-proportion-layout/feed/ 6 313543
A Guide to the Responsive Images Syntax in HTML https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/ https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/#comments Tue, 26 May 2020 22:15:50 +0000 https://css-tricks.com/?p=306139 This guide is about the HTML syntax for responsive images (and a little bit of CSS for good measure). We'll go over srcset and , plus a whole bunch of things to consider to help you get the best performance and design control from your images.


A Guide to the Responsive Images Syntax in HTML originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

This guide is about the HTML syntax for responsive images (and a little bit of CSS for good measure). The responsive images syntax is about serving one image from multiple options based on rules and circumstances. There are two forms of responsive images, and they’re for two different things:

If your only goal is…

Increased Performance

Then what you need is…

<img
  srcset=""
  src=""
  alt=""
>

There is a lot of performance gain to be had by using responsive images. Image weight has a huge impact on pages’ overall performance, and responsive images are one of the best things that you can do to cut image weight. Imagine the browser being able to choose between a 300×300 image or a 600×600. If the browser only needs the 300×300, that’s potentially a 4× bytes-over-the-wire savings! Savings generally go up as the display resolution and viewport size go down; on the smallest screens, a couple of case studies have shown byte savings of 70–90%.

If you also need…

Design Control

Then what you need is…

<picture>
  <source srcset="" media="">
  <source srcset="" media="">
  <img src="" alt="">
</picture>

Another perfectly legit goal with responsive images is not just to serve different sizes of the same image, but to serve different images. For example, cropping an image differently depending on the size of the screen and differences in the layout. This is referred to as “art direction.”

The <picture> element is also used for fallback image types and any other sort of media query switching (e.g. different images for dark mode). You get greater control of what browsers display.


There is a lot to talk about here, so let’s go through both syntaxes, all of the related attributes and values, and talk about a few related subjects along the way, like tooling and browsers.

Table of Contents


Using srcset

The <img srcset="" src="" alt=""> syntax is for serving differently-sized versions of the same image. You could try to serve entirely different images using this syntax, but browsers assume that everything in a srcset is visually-identical and will choose whichever size they think is best, in impossible-for-you-to-predict ways. So I wouldn’t recommend it.

Perhaps the easiest-possible responsive images syntax is adding a srcset attribute with x descriptors on the images to label them for use on displays with different pixel-densities.

<img 
  alt="A baby smiling with a yellow headband."
  src="baby-lowres.jpg"
  srcset="baby-highres.jpg 2x"
>

Here, we’ve made the default (the src) the “low res” (1×) copy of the image. Defaulting to the smallest/fastest resources is usually the smart choice. We also provide a 2× version. If the browser knows it is on a higher pixel-density display (the 2x part), it will use that image instead.

Demo
<img 
  alt="A baby smiling with a yellow headband."
  src="baby-lowres.jpg"
  srcset="
    baby-high-1.jpg 1.5x,
    baby-high-2.jpg 2x,
    baby-high-3.jpg 3x,
    baby-high-4.jpg 4x,
    baby-high-5.jpg 100x
  "
>

You can do as many pixel-density variants as you like.

While this is cool and useful, x descriptors only account for a small percentage of responsive images usage. Why? They only let browsers adapt based on one thing: display pixel-density. A lot of times, though, our responsive images are on responsive layouts, and the image’s layout size is shrinking and stretching right along with the viewport. In those situations, the browser needs to make decisions based on two things: the pixel-density of the screen, and the layout size of the image. That’s where w descriptors and the sizes attribute come in, which we’ll look at in the next section.

Using srcset / w + sizes

This is the good stuff. This accounts for around 85% of responsive images usage on the web. We’re still serving the same image at multiple sizes, only we’re giving the browser more information so that it can adapt based on both pixel-density and layout size.

<img 
  alt="A baby smiling with a yellow headband."
  srcset="
    baby-s.jpg  300w,
    baby-m.jpg  600w,
    baby-l.jpg  1200w,
    baby-xl.jpg 2000w
  "
  sizes="70vmin"
>

We’re still providing multiple copies of the same image and letting the browser pick the most appropriate one. But instead of labeling them with a pixel density (x) we’re labelling them with their resource width, using w descriptors. So if baby-s.jpg is 300×450, we label it as 300w.

Using srcset with width (w) descriptors like this means that it will need to be paired with the sizes attribute so that the browser will know how large of a space the image will be displaying in. Without this information, browsers can’t make smart choices.

Demo

Creating accurate sizes

Creating sizes attributes can get tricky. The sizes attribute describes the width that the image will display within the layout of your specific site, meaning it is closely tied to your CSS. The width that images render at is layout-dependent rather than just viewport dependent!

Let’s take a look at a fairly simple layout with three breakpoints. Here’s a video demonstrating this:

Demo

The breakpoints are expressed with media queries in CSS:

body {
  margin: 2rem;
  font: 500 125% system-ui, sans-serif;
}
.page-wrap {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr 200px;
  grid-template-areas:
    "header header"
    "main aside"
    "footer footer";
}

@media (max-width: 700px) {
  .page-wrap {
    grid-template-columns: 100%;
    grid-template-areas:
      "header"
      "main"
      "aside"
      "footer";
  }
}
@media (max-width: 500px) {
  body {
    margin: 0;
  }
}

The image is sized differently at each breakpoint. Here’s a breakdown of all of the bits and pieces that affect the image’s layout width at the largest breakpoint (when the viewport is wider than 700px):

The image is as wide as 100vw minus all that explicitly sized margin, padding, column widths, and gap.
  • At the largest size: there is 9rem of explicit spacing, so the image is calc(100vw - 9rem - 200px) wide. If that column used a fr unit instead of 200px, we’d kinda be screwed here.
  • At the medium size: the sidebar is dropped below, so there is less spacing to consider. Still, we can do calc(100vw - 6rem) to account for the margins and padding.
  • At the smallest size: the body margin is removed, so just calc(100vw - 2rem) will do the trick.

Phew! To be honest, I found that a little challenging to think out, and made a bunch of mistakes as I was creating this. In the end, I had this:

<img 
  ...  
  sizes="
    (max-width: 500px) calc(100vw - 2rem), 
    (max-width: 700px) calc(100vw - 6rem),
    calc(100vw - 9rem - 200px)
  "
/>

A sizes attribute that gives the browser the width of the image across all three breakpoints, factoring in the layout grid, and all of the surrounding gap, margin, and padding that end up impacting the image’s width.

Now wait! Drumroll! 🥁🥁🥁That’s still wrong. I don’t understand why exactly, because to me that looks like it 100% describes what is happening in the CSS layout. But it’s wrong because Martin Auswöger’s RespImageLint says so. Running that tool over the isolated demo reports no problems except the fact that the sizes attribute is wrong for some viewport sizes, and should be:

<img
  ...
  sizes="
    (min-width: 2420px) 2000px, 
    (min-width: 720px) calc(94.76vw - 274px), 
    (min-width: 520px) calc(100vw - 96px), 
    calc(100vw - 32px)
  "
>

I don’t know how that’s calculated and it’s entirely unmaintainable by hand, but, it’s accurate. Martin’s tool programmatically resizes the page a bunch and writes out a sizes attribute that describes the actual, observed width of the image over a wide range of viewport sizes. It’s computers, doing math, so it’s right. So, if you want a super-accurate sizes attribute, I’d recommend just putting a wrong one on at first, running this tool, and copying out the correct one.

For an even deeper dive into all this, check out Eric Portis’ w descriptors and sizes: Under the hood.

Being more chill about sizes

Another option is use the Horseshoes & Hand Grenades Method™ of sizes (or, in other words, close counts). This comes highly suggested.

For example, sizes="96vw" says, “This image is going to be pretty big on the page — almost the full width — but there will always be a little padding around the edges, so not quite. Or sizes="(min-width: 1000px) 33vw, 96vw" says, “This image is in a three-column layout on large screens and close to full-width otherwise.” Practicality-wise, this can be a sane solution.

You might find that some automated responsive image solutions, which have no way of knowing your layout, make a guess — something like sizes="(max-width: 1000px) 100vw, 1000px". This is just saying, “Hey we don’t really know much about this layout, but we’re gonna take a stab and say, worst case, the image is full-width, and let’s hope it never renders larger than 1000px”.

Abstracting sizes

I’m sure you can imagine how easy it is to not only get sizes wrong, but also have it become wrong over time as layouts change on your site. It may be smart for you to abstract it using a templating language or content filter so that you can change the value across all of your images more easily.

I’m essentially talking about setting a sizes value in a variable once, and using that variable in a bunch of different <img> elements across your site. Native HTML doesn’t offer that, but any back end language does; for instance, PHP constants, Rails config variables, the React context API used for a global state variable, or variables within a templating language like Liquid can all be used to abstract sizes.

<?php
  // Somewhere global
  $my_sizes = "";
?>

<img
  srcset=""
  src=""
  alt=""
  sizes="<?php echo $my_sizes; ?>"
/>

“Browser’s choice”

Now that we have a sizes attribute in place, the browser knows what size (or close to it) the image will render at and can work its magic. That is, it can do some math that factors in the pixel density of the screen, and the size that the image will render at, then pick the most appropriately-sized image.

The math is fairly straightforward at first. Say you’re about to show an image that is 40vw wide on a viewport that is 1200px wide, on a 2x pixel-density screen. The perfect image would be 960 pixels wide, so the browser is going to look for the closest thing it’s got. The browser will always calculate a target size that it would prefer based on the viewport and pixel-density situations, and what it knows from sizes, and compare that target to what it’s got to pick from in srcset. How browsers do the picking, though, can get a little weird.

A browser might factor more things into this equation if it chooses to. For example, it could consider the user’s current network speeds, or whether or not the user has flipped on some sort of “data saver” preference. I’m not sure if any browsers actually do this sort of thing, but they are free to if they wish as that’s how the spec was written. What some browsers sometimes choose to do is pull from cache. If the math shows they should be using a 300px image, but they already have a 600px in local cache, they will just use that. Smart. Room for this sort of thing is a strength of the srcset/sizes syntax. It’s also why you always use different sizes of the same image, within srcset: you’ve got no way to know which image is going to be selected. It’s the browser’s choice.

This is weird. Doesn’t the browser already know this stuff?

You might be thinking, “Uhm why do I have to tell the browser how big the image will render, doesn’t it know that?” Well, it does, but only after it’s downloaded your HTML and CSS and laid everything out. The sizes attribute is about speed. It gives the browser enough information to make a smart choice as soon as it sees your <img>.

<img
  data-sizes="auto"
  data-srcset="
    responsive-image1.jpg 300w,
    responsive-image2.jpg 600w,
    responsive-image3.jpg 900w"
  class="lazyload" 
/>

Now you might be thinking, “But what about lazy-loaded images?” (as in, by the time a lazy-loaded image is requested, layout’s already been done and the browser already knows the image’s render size). Well, good thinking! Alexander Farkas’ lazysizes library writes out sizes attributes automatically on lazyload, and there’s an ongoing discussion about how to do auto-sizes for lazy-loaded images, natively.

sizes can be bigger than the viewport

Quick note on sizes. Say you have an effect on your site so that an image “zooms in” when it’s clicked. Maybe it expands to fill the whole viewport, or maybe it zooms even more, so that you can see more detail. In the past, we might have had to swap out the src on click in order to switch to a higher-res version. But now, assuming a higher-res source is already in the srcset, you can just change the sizes attribute to something huge, like 200vw or 300vw, and the browser should download the super-high-res source automatically for you. Here’s an article by Scott Jehl on this technique.

↩️ Back to top


Using <picture>

Hopefully, we’ve beaten it into the ground that <img srcset="" sizes="" alt=""> is for serving differently-sized versions of the same image. The <picture> syntax can do that too, but the difference here is that the browser must respect the rules that you set. That’s useful when you want to change more than just the resolution of the loaded image to fit the user’s situation. This intentional changing of the image is usually called “art direction.”

Art Direction

<picture>
  <source 
    srcset="baby-zoomed-out.jpg"
    media="(min-width: 1000px)"
  />
  <source 
    srcset="baby.jpg"
    media="(min-width: 600px)"
  />
  <img 
    src="baby-zoomed-in.jpg" 
    alt="Baby Sleeping"
  />
</picture>

This code block is an example of what it might look like to have three stages of an “art directed” image.

  • On large screens, show a zoomed-out photo.
  • On medium screens, show that same photo, zoomed in a bit.
  • On small screens, zoom in even more.

The browser must respect our media queries and will swap images at our exact breakpoints. That way, we can be absolutely sure that nobody on a small screen will see a tiny, zoomed-out image, which might not have the same impact as one of the zoomed-in versions.

Here’s a demo, written in Pug to abstract out some of the repetitive nature of <picture>.

Art direction can do a lot more than just cropping

Although cropping and zooming like this is the most common form of art direction by far, you can do a lot more with it. For instance, you can:

Sky’s the limit, really.

Combining source and srcset

Because <source> also uses the srcset syntax, they can be combined. This means that you can still reap the performance benefits of srcset even while swapping out visually-different images with <source>. It gets pretty verbose though!

<picture>
  <source 
    srcset="
      baby-zoomed-out-2x.jpg 2x,
      baby-zoomed-out.jpg
    "
    media="(min-width: 1000px)"
  />
  <source 
    srcset="
      baby-2x.jpg 2x,
      baby.jpg
    "
    media="(min-width: 600px)"
  />
  <img 
    srcset="
      baby-zoomed-out-2x.jpg 2x
    "
    src="baby-zoomed-out.jpg"
    alt="Baby Sleeping"
  />
</picture>

The more variations you create and the more resized versions you create per variation, the more verbose this code has to get.

Fallbacks for modern image formats

The <picture> element is uniquely suited to being able to handle “fallbacks.” That is, images in cutting-edge formats that not all browsers might be able to handle, with alternative formats for browsers that can’t load the preferred, fancy one. For example, let’s say you want to use an image in the WebP format. It’s a pretty great image format, often being the most performant choice, and it’s supported everywhere that the <picture> element is, except Safari. You can handle that situation yourself, like:

<picture>
  <source srcset="party.webp">
  <img src="party.jpg" alt="A huge party with cakes.">
</picture>

This succeeds in serving a WebP image to browsers that support it, and falls back to a JPEG image, which is definitely supported by all browsers.

Here’s an example of a photograph (of me) at the exact same size where the WebP version is about 10% (!!!) of the size of the JPEG.

How do you create a WebP image? Well, it’s more of a pain in the butt than you’d like it to be, that’s for sure. There are online converters, command line tools, and some modern design software, like Sketch, helps you export in that format. My preference is to use an image hosting CDN service that automatically sends images in the perfect format for the requesting browser, which makes all this unnecessary (because you can just use img/srcset).

WebP isn’t the only player like this. Safari doesn’t support WebP, but does support a format called JPG 2000 which has some advantages over JPEG. Internet Explorer 11 happens to support an image format called JPEG-XR which has different advantages. So to hit all three, that could look like:

<picture>
  <source srcset="/images/cereal-box.webp" type="image/webp" />
  <source srcset="/images/cereal-box.jp2" type="image/jp2" />
  <img src="/images/cereal-box.jxr" type="image/vnd.ms-photo" />
</picture>

This syntax (borrowed form a blog post by Josh Comeau) supports all three of the “next-gen” image formats in one go. IE 11 doesn’t support the <picture> syntax, but it doesn’t matter because it will get the <img> fallback which is in the JPEG-XR format it understands.

Estelle Weyl also covered this idea in a 2016 blog post on image optimization.

↩️ Back to top


Where do you get the differently-sized images?

You can make them yourself. Heck, even the free Preview app on my Mac can resize an image and “Save As.”

The Mac Preview app resizing an image, which is something that literally any image editing application (including Photoshop, Affinity Designer, Acorn, etc.) can also do. Plus, they often help by exporting the variations all at once.

But that’s work. It’s more likely that the creation of variations of these images is automated somehow (see the section below) or you use a service that allows you to create variations just by manipulating the URL to the image. That’s a super common feature of any image hosting/image CDN service. To name a few:

Not only do these services offer on-the-fly image resizing, they also often offer additional stuff, like cropping, filtering, adding text, and all kinds of useful features, not to mention serving assets efficiently from a CDN and automatically in next-gen formats. That makes them a really strong choice for just about any website, I’d say.

Here’s Glen Maddern in a really great screencast talking about how useful Image CDNs can be:

Design software is becoming more aware that we often need multiple copies of images. For example, the exporting interface from Figma is pretty nice, where any given selection can be exported. It allows multiple exports at once (in different sizes and formats) and remembers what you did the last time you exported.

Exporting in Figma

Automated responsive images

The syntax of responsive images is complex to the point that doing it by hand is often out of the question. I’d highly recommend automating and abstracting as much of this away as possible. Fortunately, a lot of tooling that helps you build websites knows this and includes some sort of support for it. I think that’s great because that’s what software should be doing for us, particularly when it is something that is entirely programmatic and can be done better by code than by humans. Here are some examples…

  • Cloudinary has this responsive breakpoints tool including an API for generating the perfect breakpoints.
  • WordPress generates multiple versions of images and outputs in the responsive images syntax by default.
  • Gatsby has a grab-bag of plugins for transforming and implementing images on your site. You ultimately implement them with gatsby-image, which is a whole fancy thing for implementing responsive images and other image loading optimizations. Speaking of React, it has component abstractions like “An Almost Ideal React Image Component” that also does cool stuff.
  • Nicolas Hoizey’s Images Responsiver Node module (and it’s Eleventy plugin) makes a ton of smart markup choices for you, and pairs nicely with a CDN that can handle the on-the-fly resizing bits.
  • These are just a few examples! Literally anything you can do to make this process easier or automatic is worth doing.
Here’s me inspecting an image in a WordPress blog post and seeing a beefy srcset with a healthy amount of pre-generated size options and a sizes attribute tailored to this theme.
A landing page for gatsby-image explaining all of the additional image loading stuff it can do.

I’m sure there are many more CMSs and other software products that help automate away the complexities of creating the responsive images syntax. While I love that all this syntax exists, I find it all entirely too cumbersome to author by hand. Still, I think it’s worth knowing all this syntax so that we can build our own abstractions, or check in on the abstractions we’re using to make sure they are doing things correctly.

  • The object-fit property in CSS controls how an image will behave in its own box. For example, an image will normally “squish” if you change the dimensions to something different than its natural aspect ratio, but object-fit can be used to crop it or contain it instead.
  • The object-position property in CSS allows you to nudge an image around within its box.

What about responsive images in CSS with background images?

We’ve covered exactly this before. The trick is to use @media queries to change the background-image source. For example:

.img {
  background-image: url(small.jpg);
}
@media 
  (min-width: 468px),
  (-webkit-min-device-pixel-ratio: 2), 
  (min-resolution: 192dpi) {
  .img {
    background-image: url(large.jpg);
  }
}

With this CSS syntax, depending on the browser conditions, the browser will only download one of the two images, which achieves the same performance goal that the responsive images syntax in HTML does. If it helps, think of the above as the CSS equivalent of the <picture> syntax: the browser must follow your rules and display what matches.

If you’re looking to let the browser choose the best option, like srcset/sizes, but in CSS, the solution is ultimately going to be the image-set() function. There’s two problems with image-set(), today, though:

  • Support for it isn’t there yet. Safari’s implementation leads the pack, but image-set() has been prefixed in Chrome for eight years, and it’s not there at all in Firefox.
  • Even the spec itself seems behind the times. For example, it only supports x descriptors (no w, yet).

Best to just use media queries for now.

Do you need to polyfill?

I’m pretty meh on pollyfilling any of this right this moment. There is a great polyfill though, called Picturefill, which will buy you full IE 9-11 support if you need that. Remember, though, that none of this stuff breaks to the point of not displaying any image at all in non-supporting browsers, assuming you have an <img src="" alt=""> in there somewhere. If you make the (fairly safe) assumption that IE 11 is running on a low-pixel-density desktop display, you can make your image sources reflect that by default and build out from there.

Other important image considerations

  • Optimizing quality: The point of responsive images is loading the smallest, most impactful resource that you can. You can’t achieve that without effectively compressing your image. You’re aiming for a “sweet spot” for every image, between looking good and being light. I like to let image hosting services solve this problem for me, but Etsy has a really great writeup of what they’ve been able to accomplish with infrastructure that they built themselves.
  • Serving from CDNs: Speaking of image hosting services, speed comes in many forms. Fast servers that are geographically close to the user are an important speed factor as well.
  • Caching: What’s better than loading less data over the network? Loading no data at all! That’s what HTTP caching is for. Using the Cache-Control header, you can tell the browser to hang on to images so that if the same image is needed again, the browser doesn’t have to go over the network to get it, which is a massive performance boost for repeat viewings.
  • Lazy loading: This is another way to avoid loading images entirely. Lazy loading means waiting to download an image until it is in or near the viewport. So, for example, an image way far down the page won’t load if the user never scrolls there.

Other good resources

(That I haven’t linked up in the post already!)

Browser Support

This is for srcset/sizes, but it’s the same for <picture>.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
3838No16TP

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12612712618.0


A Guide to the Responsive Images Syntax in HTML originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/feed/ 7 https://css-tricks.com/wp-content/uploads/2020/04/grid-layout.mp4 Responsive Images Archives - CSS-Tricks nonadult 306139
Workflow Considerations for Using an Image Management Service https://css-tricks.com/workflow-considerations-for-using-an-image-management-service/ https://css-tricks.com/workflow-considerations-for-using-an-image-management-service/#comments Wed, 16 Oct 2019 15:06:11 +0000 https://css-tricks.com/?p=296693 There are all these sites out there that want to help you with your images. They do things like optimize your images and help you serve them performantly.

That's a very good thing. By any metric, images are a major slice of the resources on websites, and we're notoriously bad at optimizing them and doing all the things we could to lower the performance hit from them. So you use some service to help you deliver images better. Smart. Many of them will make managing and optimizing images a lot easier. But I don't consider them a no-brainer. There is a lot to think about, like making choices that don't paint you into a corner.


Workflow Considerations for Using an Image Management Service originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There are all these sites out there that want to help you with your images. They do things like optimize your images and help you serve them performantly.

Here’s the type of service I mean.

That’s a very good thing. By any metric, images are a major slice of the resources on websites, and we’re notoriously bad at optimizing them and doing all the things we could to lower the performance hit from them. I’m sitting at a conference right now and Dave just bet everyone in the audience $100 that he could find an unoptimized image on their site. I wasn’t about to take him up on it.

So you use some service to help you deliver images better. Smart. Many of them will make managing and optimizing images a lot easier. But I don’t consider them a no-brainer. There is a lot to think about, like making choices that don’t paint you into a corner.

I should be able to upload images from my own CMS.

I don’t want to go to your site to upload my assets. I want to use the media management in my own CMS. So, the service should have an API at a minimum, and possible even officially maintained CMS plugins.

This site uses WordPress. I can drag and drop images into the media library and posts very easily. I can search my media library for images I’ve uploaded before. I like that, and I want to take advantage of it today, and as it evolves.

The images should be uploaded to my own server.

If it also has to be uploaded to the image service, that’s fine. But it should go to my server first, then to the service. That way, I still maintain ownership of the source file.

Images within content should use functional, semantic markup in my CMS.

I’d prefer that the images within content are stored as totally functional HTML in my database:

<img src="/images/flower.jpg" alt="a blue flower">

It could be fancier than that, like using srcset (but probably not sizes as that will change as the design changes), or be contained within <picture> or <figure> elements… whatever you like that makes sense as semantic HTML. The most important thing being that the content in my database has fully functional HTML with a src on the image that points to a real image on my real server.

The implementation of the image service will involve filtering that HTML to do whatever it needs to do, like replace the URLs to generate fancier responsive image markup and whatnot.

Between having functional HTML and images on my server, that enables me to turn off the image service if I need to. Services have a habit of coming and going, or changing in ways that make them more or less palatable. I don’t want to be locked-in; I want freedom. I want to be able turn off the service and have a perfectly functional site with perfectly functional images, and not be obstructed from moving to a different service — or no service at all.

Even if I didn’t use the service in the past, I want all my images to benefit from it.

I just mentioned filtering the HTML for images in my database. That should happen for all the images on my site, even if they were uploaded and used before I started using the image service.

This probably means the services offers a URL-based “get” API to optimize images on-the-fly pulled from their canonical locations.

I shouldn’t have to think about format or size.

I want to upload whatever I have. Probably some huge un-optimized screenshot I just took. If I think about it at all, I want to upload something much too big and much too high-quality so that I know I have a great original version available. The service will create optimized, sized, and formatted images as needed.

I also want to upload SVG and have it stay SVG (that’s also optimized).

The images will ultimately be served on a CDN.

CDNs are vital for speed. Australians get images from servers hosted in Australia. Canadians get images from servers hosted in Canada. The servers are configured to be fast and cookie-less and all the fancy over-my-head things that make an asset CDN scream.

The images should serve in the right format.

If you serve images in WebP format to browsers that support it, you’ll probably get as much or more performance out of that optimization than serving re-sized images with responsive images syntax. It’s a big deal.

I want the service to know what the best possible format for any particular image for any particular browser and serve the image in that format. This is going to change over time, so I want the service to stay on top of this so I don’t have to.

I know that involved formats like JPEG-XR and JPEG-2000 three years ago. Is that still the case? I have no idea. This is a core value proposition for the service.

It should optimize the images and handle quality.

This is perhaps the most obvious feature and the reason you reach for an image service in the first place. Images need optimization. There are perhaps dozens of image optimization tools/algorithms that aim to squeeze every last byte out of images. The image service probably uses those or even has its own fancy tech for it. Ideally, the default is to optimize an image the most it possibly can be without noticeably hurting the quality, but still allowing me to ratchet it down even more if I want to.

Don’t shame me for using high-pixel density images.

A lot of image services have some sort of tester tool where you drop in a URL and it tells you how bad you’re doing with images. Many of them test the size of the image on the rendered page and compare the dimensions of the original image. If the original image is larger, they tell you could have had savings by sizing it down. That’s obnoxious to me. High-pixel density displays have been around for a long time and it’s no crime to serve them.

It should help me serve the right size for the device it’s on and the perfect responsive syntax if needed.

Not all images benefit from the same responsive breakpoints. Check out the site Responsive Image Breakpoints. It generates versions of the image that are best depending on the image itself. That’s the kind of help I like to see from an image service. Take something hard and automate it for me.

I know I’ll probably need to bring my own sizes attribute because that is very dependant on my own CSS and how the design of the site plays out. It’s still important, and makes me wonder if an image service could step up and help me figure out what my optimal sizes attribute should be for certain images. Like loading my site at different sizes and seeing how large the image renders with my CSS and calculating it from there to use later.

Just me.

This is just my own list of requirements. I feel like it’s fairly reflective of “normal” sites that have a bunch of images and want to do the right thing to serve them.

I didn’t go into all the fancy features image services offer, like being able to tell you that an image contains a giraffe facing west and hasn’t eaten since Thursday while offering to recolor its retinas. I know those things are vital to some companies. This is more about what seems to me the widest and most common use case of just hosting and delivering images in the best way current technology allows.


Workflow Considerations for Using an Image Management Service originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/workflow-considerations-for-using-an-image-management-service/feed/ 5 296693
Getting Netlify Large Media Going https://css-tricks.com/getting-netlify-large-media-going/ https://css-tricks.com/getting-netlify-large-media-going/#comments Tue, 20 Aug 2019 21:51:51 +0000 https://css-tricks.com/?p=294701 (This is a sponsored post.)

I just did this the other day so I figured I’d blog it up. There is a thing called Git Large File Storage (Git LFS). Here’s the entire point of it: it keeps …


Getting Netlify Large Media Going originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
(This is a sponsored post.)

I just did this the other day so I figured I’d blog it up. There is a thing called Git Large File Storage (Git LFS). Here’s the entire point of it: it keeps large files out of your repo directly. Say you have 500MB of images on your site and they kinda need to be in the repo so you can work with it locally. But that sucks because someone cloning the repo needs to download a ton of data. Git LFS is the answer.

Netlify has a product on top of Git LFS called Large Media. Here’s the entire point of it: In addition to making it all easier to set up and providing a place to put those large files, once you have your files in there, you can URL query param based resizing on them, which is very useful. I’m all about letting computers do my image sizing for me.

You should probably just read the docs if you’re getting started with this. But I ran into a few snags so I’m jotting them down here in case this ends up useful.

You gotta install stuff

I’m on a Mac, so these are the things I did. You’ll need:

  1. Git LFS itself: brew install git-lfs
  2. Netlify CLI: npm install netlify-cli -g
  3. Netlify Large Media add-on for the CLI: netlify plugins:install netlify-lm-plugin and then netlify lm:install

“Link” the site

You literally have to auth on Netlify and that connects Netlify CLI to the site you’re working on.

netlify link

It will create a .netlify/state.json file in your project like this:

{
	"siteId": "xxx"
}

Run setup

netlify lm:setup

This creates another file in your project at .lsfconfig:

[lfs]
	url = https://xxx.netlify.com/.netlify/large-media

You should commit them both.

“Track” all your images

You’ll need to run more terminal commands to tell Netlify Large Media exactly which images should be on Git LFS. Say you have a bunch of PNGs and JPGs, you could run:

git lfs track "*.jpg" "*.png"

This was a minor gotcha for me. My project had mostly .jpeg files and I got confused why this wasn’t picking them up. Notice the slightly different file extension (ughadgk).

This will make yet another file on your project called .gitattributes. In my case:

*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text

This time, when you push, all the images will upload to the Netlify Large Media storage service. It might be an extra-slow seeming push depending on how much you’re uploading.

My main gotcha was at this point. This last push would just spin and spin for me and my Git client and eventually fail. Turns out I needed to install the netlify-credential-helper. It worked fine after I did that.

And for the record, it’s not just images that can be handled this way, it’s any large file. I believe they are called “binary” files and are what Git isn’t particularly good at handling.

Check out your repo, the images are just pointers now

Git repo where a JPG file isn’t actually an image, but a small text pointer.

And the best part

To resize the image on-the-fly to the size I want, I can do it through URL params:

<img 
  src="slides/Oops.003.jpeg?nf_resize=fit&w=1000"
  alt="Screenshots of CSS-Tricks and CodePen homepages"
/>

Which is superpowered by a responsive images syntax. For example…

<img srcset="img.jpg?nf_resize=fit&w=320 320w,
             img.jpg?nf_resize=fit&w=480 480w,
             img.jpg?nf_resize=fit&w=800 800w"
      sizes="(max-width: 320px) 280px,
             (max-width: 480px) 440px,
             800px"
        src="img.jpg?nf_resize=fit&w=800" alt="Elva dressed as a fairy">

Getting Netlify Large Media Going originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/getting-netlify-large-media-going/feed/ 1 294701
Tips for rolling your own lazy loading https://css-tricks.com/tips-for-rolling-your-own-lazy-loading/ https://css-tricks.com/tips-for-rolling-your-own-lazy-loading/#comments Mon, 01 Jul 2019 14:16:24 +0000 http://css-tricks.com/?p=291777 You may have heard (or even issued the call) that “we can just use lazy loading!” when looking for a way to slim down a particularly heavy web page.

Lazy loading is a popular technique for gradually requesting images as …


Tips for rolling your own lazy loading originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You may have heard (or even issued the call) that “we can just use lazy loading!” when looking for a way to slim down a particularly heavy web page.

Lazy loading is a popular technique for gradually requesting images as they come into view, rather than all at once after the HTML of the page has been parsed. It can reduce the initial page weight, and help us hit our performance budgets by requesting images when they’re needed.

It can be effective. But it also comes with some baggage of its own. We’ll get to that! In fact, Rahul Nanwani did an extensive write-up that hits several lazy-loading methods and illustrates just how complex some are.

In this post, we’ll look at an implementation that’s already been covered in brief detail in this post by Preethi. We’re going to expand on that so you can add your own implementation of lazy loading to your site as I’ve done on this little demo site.

We’ll cover these topics along the way:

Here’s what we’ll be building:

Why not native lazy loading?

Currently, lazy loading is not something browsers can do for us natively. Although that looks set to change soon for some browsers with the launch of Chrome 75 which aims to bring lazy loading support for images and iframes. Until that time (and beyond, if we are to play nicely with other browsers – which we should) lazy loading is implemented using JavaScript. There are a number of libraries and frameworks out there to help.

Some static site generators, libraries, and frameworks include utilities to provide this capability “out of the box”, which proves popular as people look for built-in ways to include this feature in their sites. But I’ve also noticed a trend where some chose to adopt entire libraries or frameworks in order to gain access to just this feature. As a thrifty, performance and inclusivity blow-hard, I’m a bit cautious about this. So let’s look at how you could implement this yourself without the need for a specific framework or library.

lazy loading example

The typical mechanics for lazy loading

Most approaches follow a pattern like this:

First, some HTML to define our lazily loaded images

<!-- 
Don't include a src attribute in images you wish to load lazily.
Instead specify their src safely in a data attribute
-->
<img data-src="lighthouse.jpg" alt="A snazzy lighthouse" class="lazy" />

When should image loading take place?

Next, we use some sort of JavaScript magic to correctly set the src attribute when the image comes into view. This was once an expensive JavaScript operation, involving listening for window scroll and resize events, but IntersectionObserver has come to the rescue.

Creating an intersection observer looks like this:

// Set up an intersection observer with some options
var observer = new IntersectionObserver(lazyLoad, {

  // where in relation to the edge of the viewport, we are observing
  rootMargin: "100px",

  // how much of the element needs to have intersected 
  // in order to fire our loading function
  threshold: 1.0

});

We’ve told our new observer to call a function named lazyLoad when those observable conditions are met. The elements that satisfy those conditions will be passed to that function so that we can manipulate them… like actually load and display them.

function lazyLoad(elements) {
  elements.forEach(image => {
    if (image.intersectionRatio > 0) {

      // set the src attribute to trigger a load
      image.src = image.dataset.src;

      // stop observing this element. Our work here is done!
      observer.unobserve(item.target);
    };
  });
};

Great. Our images will have the correct src assigned to them as they come into view, which will cause them to load. But which images? We need to tell the Intersection Observer API which elements we care about. Luckily, we assigned each one with a CSS class of .lazy for just this purpose.

// Tell our observer to observe all img elements with a "lazy" class
var lazyImages = document.querySelectorAll('img.lazy');
lazyImages.forEach(img => {
  observer.observe(img);
});

Nice. But perfect?

This seems to be working nicely, but there are some drawbacks to consider:

  1. Until (or unless) JavaScript comes along and successfully runs, we have a bunch of image elements on our page that will not work. We deliberately nixed them by removing the src attribute. That’s the result we wanted, but now we are dependent on JavaScript for these images to load. While it’s true that JavaScript is pretty well ubiquitous on the web theses days — with the web reaching such a broad spectrum of devices and network conditions — JavaScript can become an expensive addition to our performance budgets, particularly if it is involved in the delivery and rendering of content. As Jake Archibald once pointed out, all your users are non-JS while they’re downloading your JS. In other words, this is not to be taken lightly.
  2. Even when this works successfully, we have empty elements on our page which might give a bit of a visual jolt when they load in. Perhaps we can hint at the image first and do something fancy. We’ll get to that shortly.

The planned native lazy loading implementation by Chrome should help to address our first point here. If the element has been given a loading attribute, Chrome can honor the src attribute specified at the right time, rather than requesting it eagerly the moment it sees it in the HTML.

The editor’s draft of the specification includes support for different loading behaviors:

  • <img loading="lazy" />: Tell the browser to load this image lazily when needed.
  • <img loading="eager" />: Tell the browser to load this image immediately.
  • <img loading="auto" />: Let the browser make its own assessment.

Browsers without this support would be able to load the image as normal thanks to the resilient nature of HTML and browsers ignoring HTML attributes that they don’t understand.

But… sound the loud caution klaxon! This feature has yet to land in Chrome, and there is also uncertainty about if and when other browsers might choose to implement it. We can use feature detection to decide which method we use, but this still doesn’t give a solid progressive enhancement approach where the images have no dependency on JavaScript.

<img data-src="lighthouse.jpg" alt="A snazzy lighthouse" loading="lazy" class="lazy" />
// If the browser supports lazy loading, we can safely assign the src
// attributes without instantly triggering an eager image load.
if ("loading" in HTMLImageElement.prototype) {
  const lazyImages = document.querySelectorAll("img.lazy");
  lazyImages.forEach(img => {
    img.src = img.dataset.src;
  });
}
else {
  // Use our own lazyLoading with Intersection Observers and all that jazz
}

As a companion to responsive images

Assuming that we are comfortable with the fact that JavaScript is a dependency for the time being, let’s turn our attention to a related topic: responsive images.

If we’re going through the trouble of delivering images into the browser only when needed, it seems fair that we might also want to make sure that we are also delivering them in the best size for how they’ll be displayed. For example, there’s no need to download the 1200px-wide version of an image if the device displaying it will only give it a width of 400px. Let’s optimize!

HTML gives us a couple of ways to implement responsive images which associate different image sources to different viewport conditions. I like to use the picture element like this:

<picture>
  <source srcset="massive-lighthouse.jpg" media="(min-width: 1200px)">
  <source srcset="medium-lighthouse.jpg" media="(min-width: 700px)">
  <source srcset="small-lighthouse.jpg" media="(min-width: 300px)">
  <img src="regular-lighthouse.jpg" alt="snazzy lighthouse" />
</picture>

You’ll notice that each source element has a srcset attribute which specifies an image URL, and a media attribute that defines the conditions under which this source should be used. The browser selects the most suitable source from the list according to the media conditions with a standard img element acting as a default/fallback.

Can we combine these two approaches to make lazy-loading responsive images?

Of course, we can! Let’s do it.

Instead of having an empty image until we do our lazy load, I like to load a placeholder image that has a tiny file size. This does incur the overhead of making more HTTP requests, but it also gives a nice effect of hinting at the image before it arrives. You might have seen this effect on Medium or as a result of a site using Gatsby’s lazy loading mechanics.

We can achieve that by initially defining the image sources in our picture element as tiny versions of the same asset and then using CSS to scale them to the same size as their higher-resolution brothers and sisters. Then, through our intersection observer, we can update each of the specified sources to point at the correct image sources.

So, initially our picture element might look like this:

<picture class="lazy">
  <source srcset="tiny-lighthouse.jpg" media="(min-width: 1200px)">
  <source srcset="tiny-lighthouse.jpg" media="(min-width: 700px)">
  <source srcset="tiny-lighthouse.jpg" media="(min-width: 300px)">
  <img src="tiny-lighthouse.jpg" alt="snazzy cake" />
</picture>

No matter what viewport size is applied, we’ll display a tiny 20px image. We’re going to blow it up with CSS next.

Previewing the image with style

The browser can scale up the tiny preview image for us with CSS so that it fits the entire picture element rather than a mere 20px of it. Things are going to get a little… pixelated, as you may imagine when a low-resolution image is blown up to larger dimensions.

picture {
  width: 100%; /* stretch to fit its containing element */
  overflow: hidden;
}

picture img {
  width: 100%; /* stretch to fill the picture element */
}
blurred image

For good measure, we can soften that pixelation introduced by scaling up the image by using a blur filter.

picture.lazy img {
  filter: blur(20px);
}
more fully blurred image

Switching sources with JavaScript

With a little adaptation, we can use the same technique as before to set the correct URLs for our srcset and src attributes.

function lazyLoad(elements) {

  elements.forEach(picture => {
    if (picture.intersectionRatio > 0) {

      // gather all the image and source elements in this picture
      var sources = picture.children;

      for (var s = 0; s < sources.length; s++) {
        var source = sources[s];

        // set a new srcset on the source elements 
        if (sources.hasAttribute("srcset")) {
          source.setAttribute("srcset", ONE_OF_OUR_BIGGER_IMAGES);
        }
        // or a new src on the img element
        else {
          source.setAttribute("src", ONE_OF_OUR_BIGGER_IMAGES);
        }
      }

      // stop observing this element. Our work here is done!
      observer.unobserve(item.target);
    };
  });

};

One last step to complete the effect: remove that blur effect from the image once the new source has loaded. A JavaScript event listener waiting for the load event on each new image resource can do that for us.

// remove the lazy class when the full image is loaded to unblur
source.addEventListener('load', image => {
  image.target.closest("picture").classList.remove("lazy")
}, false);

We can make a nice transition that eases away the blur away, with a sprinkle of CSS.

picture img {
  ...
  transition: filter 0.5s,
}

A little helper from our friends

Great. With just a little JavaScript, a few lines of CSS and a very manageable dollop of HTML, we’ve created a lazy loading technique which also caters for responsive images. So, why aren’t we happy?

Well, we’ve created two bits of friction:

  1. Our markup for adding images is more complex than before. Life used to be simple when all we needed was a single img tag with a good old src attribute.
  2. We’ll also need to create multiple versions of each image assets to populate each viewport size and the pre-loaded state. That’s more work.

Never fear. We can streamline both of these things.

Generating the HTML elements

Let’s look first at generating that HTML rather than authoring it by hand each time.

Whatever tool you use to generate your HTML, chances are that it includes a facility to use includes, functions, shortcodes, or macros. I’m a big fan of using helpers like this. They keep more complex or nuanced code fragments consistent and save time from having to write lengthy code. Most static site generators have this sort of ability.

  • Jekyll lets you create custom Plugins
  • Hugo gives you custom shortcodes
  • Eleventy has shortcodes for all of the template engines it supports
  • There are many more…

As an example, I made a shortcode called lazypicture in my example project built with Eleventy. The shortcode gets used like this:

{% lazypicture lighthouse.jpg "A snazzy lighthouse" %}

To generate the HTML that we need at build time:

<picture class="lazy">
  <source srcset="/images/tiny/lighthouse.jpg" media="(min-width: 1200px)">
  <source srcset="/images/tiny/lighthouse.jpg" media="(min-width: 700px)">
  <source srcset="/images/tiny/lighthouse.jpg" media="(min-width: 300px)">
  <img src="/images/tiny/lighthouse.jpg" alt="A snazzy lighthouse" />
</picture>

Generating the image assets

The other bit of work we have created for ourselves is generating differently sized image assets. We don’t want to manually create and optimize each and every size of every image. This task is crying out for some automation.

The way you choose to automate this should take into account the number of images assets you need and how regularly you might add more images to that set. You might chose to generate those images as part of each build. Or you could make use of some image transformation services at request time. Let’s look a little at both options.

Option 1: Generating images during your build

Popular utilities exist for this. Whether you run your builds with Grunt, Gulp, webpack, Make, or something else, chances are there is utility for you.

The example below is using gulp-image-resize in a Gulp task as part of a Gulp build process. It can chomp through a directory full of image assets and generate the variants you need. It has a bunch of options for you to control, and you can combine with other Gulp utilities to do things like name the different variants according to the conventions you choose.

var gulp = require('gulp');
var imageResize = require('gulp-image-resize');

gulp.task('default', function () {
  gulp.src('src/**/*.{jpg,png}')
    .pipe(imageResize({
      width: 100,
      height: 100
    }))
    .pipe(gulp.dest('dist'));
});

The CSS-Tricks site uses a similar approach (thanks to the custom sizes feature in WordPress) to auto-generate all of its different image sizes. (Oh yeah! CSS-Tricks walks the walk!) ResponsiveBreakpoints.com provides a web UI to experiment with different settings and options for creating images sets and even generates the code for you.

Or, you can use it programmatically as Chris mentioned on Twitter.

When you have as many image files as CSS-Tricks, though, doing this work as part of a build step can become cumbersome. Good caching in your build and other file management tasks can help, but it can be easy to end up with a lengthy build process that heats up your computer as it performs all of the work.

An alternative is to transform these resources at request time rather than during a build step. That’s the second option.

Option 2: On-demand image transformations

I’m a loud advocate of pre-rendering content. I’ve shouted about this approach (often referred to as JAMstack) for quite some time, and I believe that it has numerous performance, security and simplicity benefits. (Chris summed this up nicely in a post about static hosting and JAMstack.)

That said, the idea of generating different image sizes at request time might seem to be contrary to my lazy loading objectives. In fact, there are a number of services and companies now, who specialize in this and they do it in a very powerful and convenient way.

Combining image transformations with powerful CDN and asset caching capabilities by companies like Netlify, Fastly, and Cloudinary can rapidly generate images with the dimensions you pass to them via a URL. Each service has significant processing power to perform these transformations on the fly, then cache the generated images for future use. This makes for seamless rendering for subsequent requests.

Since I work at Netlify, I’ll illustrate this with an example using Netlify’s service. But the others I mentioned work in similar ways.

Netlify’s Image Transformation service builds on top of something called Netlify Large Media. This is a feature created to help manage large assets in your version control. Git is not very good at this by default, but Git Large File Storage can extend Git to make it possible to include large assets in your repos without clogging them up and making them unmanageable.

You can read more on the background of that approach for managing large assets if you are interested.

Placing images under version control in our Git repositories is an added bonus, but for our purposes, we are more interested in enjoying the benefits of making on-the-fly transformations of those images.

Netlify looks for querystring parameters when transforming images. You can specify the height, width and the type of crop you’d like to perform. Like this:

  • A raw image with no transformations:
    /images/apple3.jpg
  • An image resized to be 300px wide:
    /images/apple3.jpg?nf_resize=fit&w=300
  • An image cropped to be 500px by 500px with automated focal point detection:
    /images/apple3.jpg?nf_resize= smartcrop&w=500&h=500

Knowing that we can create and deliver any image sizes from a single source image in our version control means that the JavaScript we use to update the image sources only need to include the size parameters we choose.

The approach can drastically speed up your site build processes because the work is now outsourced and not performed at build time.

Wrapping it all up

We’ve covered a lot of ground here. There are a lot of very achievable options for implementing responsive images with lazy loading. Hopefully, this will give enough info to make you think twice about reaching for the nearest available framework to gain access to this sort of functionality.

This demo site pulls together a number of these concepts and uses Netlify’s image transformation service.

One last time, to summarize the flow

  • A static site generator with a shortcode eases the task of creating the picture elements
  • Netlify Large Media hosts and transforms the images, then serves them as tiny 20px-wide versions before the larger files are loaded as needed.
  • CSS scales up the tiny images and blurs them to create the preview placeholder images.
  • The Intersection Observer API detects when to swap the image assets for the appropriate larger versions.
  • JavaScript detects the load event for the larger images and removes out the blur effect to reveal the higher-resolution rendering.

Tips for rolling your own lazy loading originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/tips-for-rolling-your-own-lazy-loading/feed/ 12 291777
Planning for Responsive Images https://css-tricks.com/planning-for-responsive-images/ https://css-tricks.com/planning-for-responsive-images/#comments Wed, 13 Mar 2019 14:33:22 +0000 http://css-tricks.com/?p=283356 The first time I made an image responsive, it was as simple as coding these four lines:

img {
  max-width: 100%;
  height auto; /* default */
}

Though that worked for me as a developer, it wasn’t the best for …


Planning for Responsive Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The first time I made an image responsive, it was as simple as coding these four lines:

img {
  max-width: 100%;
  height auto; /* default */
}

Though that worked for me as a developer, it wasn’t the best for the audience. What happens if the the image in the src attribute is heavy? On high-end developer devices (like mine with 16GB RAM), few or no performance problems occur. But on low-end devices? It’s another story.

image at multiple screen sizes

The above illustration isn’t detailed enough. I’m from Nigeria and, if your product works in Africa, then you shouldn’t be looking at that. Look at this graph instead:

Nowadays, the lowest-priced iPhone sells for an average of $300. The average African can’t afford it even though iPhone is a threshold for measuring fast devices.

That’s all the business analysis you need to understand that CSS width doesn’t cut it for responsive images. What would, you ask? Let me first explain what images are about.

Nuances of images

Images are appealing to users but are a painstaking challenge for us developers who must consider the following factors:

  • Format
  • Disk size
  • Render dimension (layout width and height in the browser)
  • Original dimension (original width and height)
  • Aspect ratio

So, how do we pick the right parameters and deftly mix and match them to deliver an optimal experience for your audience? The answer, in turn, depends on the answers to these questions:

  • Are the images created dynamically by the user or statically by a design team?
  • If the width and height of the image are changed disproportionately, would that affect the quality?
  • Are all the images rendered at the same width and height? When rendered, must they have a specific aspect ratio or one that’s entirely different?
  • What must be considered when presenting the images on different viewports?

Jot down your answers. They will not only help you understand your images — their sources, technical requirements and such — but also enable you to make the right choices in delivery.

Provisional strategies for image delivery

Image delivery has evolved from a simple addition of URLs to the src attribute to complex scenarios. Before delving into them, let’s talk about the multiple options for presenting images so that you can devise a strategy on how and when to deliver and render yours.

First, identify the sources of the images. That way, the number of obscure edge cases can be reduced and the images can be handled as efficiently as possible.

In general, images are either:

  • Dynamic: Dynamic images are uploaded by the audience, having been generated by other events in the system.
  • Static: A photographer, designer, or you (the developer) create the images for the website.

Let’s dig into strategy for each of this types of images.

Strategy for dynamic images

Static images are fairly easy to work with. On the other hand, dynamic images are tricky and prone to problems. What can be done to mitigate their dynamic nature and make them more predictable like static images? Two things: validation and intelligent cropping.

Validation

Set out a few rules for the audience on what is acceptable and what is not. Nowadays, we can validate all the properties of an image, namely:

  • Format
  • Disk size
  • Dimension
  • Aspect ratio

Note: An image’s render size is determined during rendering, hence no validation on our part.

After validation, a predictable set of images would emerge, which are easier to consume.

Intelligent Cropping

Another strategy for handling dynamic images is to crop them intelligently to avoid deleting important content and refocus on (or re-center) the primary content. That’s hard to do. However, you can take advantage of the artificial intelligence offered by open-source tools or SaaS companies that specialize in image management. An example is in the upcoming sections.


Once a strategy has been nailed down for dynamic images, create a rule table with all the layout options for the images. Below is an example. It’s even worth looking into analytics to determine the most important devices and viewport sizes.

Browser Viewport HP Laptop PS4 Slim Camera Lens / Aspect Ratio
< 300 100 vw 100 vw 100 vw/1:2
300 – 699 100 vw 100 vw 100 vw/1:1
700 – 999 50 vw 50 vw 50 vw/1:1
> 999 33 vw 33 vw 100 vw/1:2

The bare (sub-optimal) minimum

Now set aside the complexities of responsiveness and just do what we do best — simple HTML markup with maximum-width CSS.

The following code renders a few images:

<main>

<figure>
    <img src="https://res.cloudinary.com/...w700/ps4-slim.jpg" alt="PS4 Slim">
</figure>

      

<figure>
    <img src="https://res.cloudinary.com/...w700/x-box-one-s.jpg" alt="X Box One S">
</figure>

      
  <!-- More images -->
    

<figure>
    <img src="https://res.cloudinary.com/...w700/tv.jpg" alt="Tv">
</figure>

</main>

Note: The ellipsis (…) in the image URL specifies the folder, dimension, and cropping strategy, which are too much detail to include, hence the truncation to focus on what matters now. For the complete version, see the CodePen example down below.

This is the shortest CSS example on the Internet that makes images responsive:

/* The parent container */
main {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

img {
  max-width: 100%;
}

If the images do not have a uniform width and height, replace max-width with object-fit and set the value to cover.

Jo Franchetti’s blog post on common responsive layouts with CSS Grid explains how the value of grid-template-columns makes the entire layout adaptive (responsive).

See the Pen
Grid Gallery
by Chris Nwamba (@codebeast)
on CodePen.

The above is not what we are looking for, however, because…

  • the image size and weight are the same on both high-end and low-end devices, and
  • we might want to be stricter with the image width instead of setting it to 250 and letting it grow.

Well, this section covers “the bare minimum” so that’s it.

Layout variations

The worst thing that can happen to an image layout is mismanagement of expectations. Because images might have varying dimensions (width and height), we must specify how to render the images.

Should we intelligently crop all the images to a uniform dimension? Should we retain the aspect ratio for a viewport and alter the ratio for a different one? The ball is in our court.

In case of images in a grid, such as those in the example above with different aspect ratios, we can apply the technique of art direction to render the images. Art direction can help achieve something like this:

For details on resolution switching and art direction in responsive images, read Jason Grigsby’s series. Another informative reference is Eric Portis’s Responsive Images Guide, parts 1, 2, and 3.

See the code example below.

<main>

<figure>
    <picture>
      <source media="(min-width: 900px)" srcset="https://res.cloudinary.com/.../c_fill,g_auto,h_1400,w_700/camera-lens.jpg">
      
      <img src="https://res.cloudinary.com/.../c_fill,g_auto,h_700,w_700/camera-lens.jpg" alt="Camera lens">
    </picture>
</figure>

  

<figure>
    <picture>
      <source media="(min-width: 700px)" srcset="https://res.cloudinary.com/.../c_fill,g_auto,h_1000,w_1000/ps4-pro.jpg">
    </picture>
    <img src="https://res.cloudinary.com/.../c_fill,g_auto,h_700,w_700/ps4-pro.jpg" alt="PS4 Pro">
</figure>

</main>

Instead of rendering only one 700px wide image, we render 700px x 700px only if the viewport width exceeds 700px. If the viewport is larger, then the following rendering occurs:

  • Camera lens images are rendered as a portrait image of 700px in width and 1000px. in height (700px x 1000px).
  • PS4 Pro images are rendered at 1000px x 1000px.

Art direction

By cropping images to make them responsive, we might inadvertently delete the primary content, like the face of the subject. As mentioned previously, AI open-source tools can help crop intelligently and refocus on the primary objects of images. In addition, Nadav Soferman’s post on smart cropping is a useful start guide.

Strict grid and spanning

The first example on responsive images in this post is a flexible one. At a minimum of 300px width, grid items automagically flow into place according to the viewport width. Terrific.

On the other hand, we might want to apply a stricter rule to the grid items based on the design specifications. In that case, media queries come in handy.

Alternatively, we can leverage the grid-span capability to create grid items of varied widths and lengths:

@media(min-width: 700px) {
  main {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
}

@media(min-width: 900px) {
  main {
    display: grid;
    grid-template-columns: repeat(3, 1fr)
  }
  figure:nth-child(3) {
    grid-row: span 2;
  }
  figure:nth-child(4) {
    grid-column: span 2;
    grid-row: span 2;
  }
}

For an image that is 1000px x 1000px square on a wide viewport, we can span it to take two grid cells on both row and column. The image that changes to a portrait orientation (700px x 1000px) on a wider viewport can take two cells on a row.

See the Pen
Grid Gallery [Art Direction]
by Chris Nwamba (@codebeast)
on CodePen.

Progressive optimization

Blind optimization is as lame as no optimization. Don’t focus on optimization without predefining the appropriate measurements. And don’t optimize if the optimization is not backed by data.

Nonetheless, ample room exists for optimization in the above examples. We started with the bare minimum, showed you some cool tricks, and now we have a working, responsive grid. The next question to ask is, “If the page contains 20-100 images, how good will the user experience be?”

Here’s the answer: We must ensure that in the case of numerous images for rendering, their size fits the device that renders them. To accomplish that, we need to specify the URLs of several images instead of one. The browser would pick the right (most optimal) one according to the criteria. This technique is called resolution switching in responsive images. See this code example:

<img srcset="https://res.cloudinary.com/.../h_300,w_300/v1548054527/ps4.jpg 300w,
          https://res.cloudinary.com/.../h_700,w_700/v1548054527/ps4.jpg 700w,
          https://res.cloudinary.com/.../h_1000,w_1000/v1548054527/ps4.jpg 1000w" sizes="(max-width: 700px) 100vw, (max-width: 900px) 50vw, 33vw" src="https://res.cloudinary.com/.../h_700,w_700/v1548054527/ps4.jpg 700w" alt="PS4 Slim">

Harry Roberts’s tweet intuitively explains what happens:

When I first tried resolution switching, I got confused and tweeted:

Hats off to Jason Grigsby for the clarification in his replies.

Thanks to resolution switching, if the browser is resized, then it downloads the right image for the right viewport; hence small images for small phones (good on CPU and RAM) and larger images for larger viewports.

The above table shows that the browser downloads the same image (blue rectangle) with different disk sizes (red rectangle).

See the Pen
Grid Gallery [Optimized]
by Chris Nwamba (@codebeast)
on CodePen.

Cloudinary’s open-source and free Responsive Image Breakpoints Generator is extremely useful for adapting website images to multiple screen sizes. However, in many cases, setting srcset and sizes alone would suffice.

Conclusion

This article aims at affording simple yet effective guidelines for setting up responsive images and layouts in light of the many—and potentially confusing—options available. Do familiarize yourself with CSS grid, art direction, and resolution switching and you’ll be a ninja in short order. Keep practicing!


Planning for Responsive Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/planning-for-responsive-images/feed/ 2 283356
FUIF: Responsive Images by Design https://css-tricks.com/fuif-responsive-images-by-design/ Tue, 27 Nov 2018 15:11:04 +0000 http://css-tricks.com/?p=279346 Jon Sneyers:

One of the main motivations for FUIF is to have an image format that is responsive by design, which means it’s no longer necessary to produce many variants of the same image: low-quality placeholders, thumbnails, many downscaled


FUIF: Responsive Images by Design originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Jon Sneyers:

One of the main motivations for FUIF is to have an image format that is responsive by design, which means it’s no longer necessary to produce many variants of the same image: low-quality placeholders, thumbnails, many downscaled versions for many display resolutions. A single file, truncated at different offsets, can do the same thing.

FUIF isn’t anywhere near ready to use, but it’s a fascinating idea. I love the idea that the format stores the image data in such a way that you request just first few kilobytes of the file and to essentially get a low-quality version, then you request more as needed. See this little demo from Eric Portis that shows it off somewhat via a Service Worker and a progressive JPG.

If this idea ever does get legs and support in browsers, Cloudinary is super well suited to take advantage of that, as they serve the best image format for the current browser — and that is massive for image performance.

To Shared LinkPermalink on CSS-Tricks


FUIF: Responsive Images by Design originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
279346
Sometimes `sizes` is quite important. https://css-tricks.com/sometimes-sizes-is-quite-important/ https://css-tricks.com/sometimes-sizes-is-quite-important/#comments Fri, 27 Jul 2018 14:26:08 +0000 http://css-tricks.com/?p=273936 Paraphrased question from email:

I just read your article Responsive Images: If you’re just changing resolutions, use srcset. In the age of “responsive websites,” srcset does not help in certain situations. For example, I have a popular products slider.


Sometimes `sizes` is quite important. originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Paraphrased question from email:

I just read your article Responsive Images: If you’re just changing resolutions, use srcset. In the age of “responsive websites,” srcset does not help in certain situations. For example, I have a popular products slider. On mobile, I have one image per slide where the images are 320px wide. On desktop, I have six images per slide where each is 160px wide. So the desktop images are smaller on desktop, not bigger.

How do I handle this situation with srcset?

I tried to be careful with that post title: “If you’re just changing resolutions, use srcset.” In this case, we’re changing the size of the images not just at certain resolutions, but at specific breakpoints as well, which means we’re also going to need to use the sizes attribute to get the most out of responsive images. The entire job of the sizes attribute is to tell the browser what size the image will be shown at, as per our CSS.

A demo! Resize the width to see it alternate between the “Desktop” and “Mobile” views.

See the Pen Responsive Images Slider by Chris Coyier (@chriscoyier) on CodePen.

As the email mentioned, the “Desktop” version actually renders the images smaller (160px) than the “Mobile” version (320px).

Let’s also account for 2x displays. To prepare ourselves, let’s have three versions of each image:

  • 160px (for 1x desktop displays)
  • 320px (for 2x desktop displays, or 1x mobile displays)
  • 640px (for 2x mobile displays)

With srcset, that looks like this:

<img srcset="
  food-big.jpg 640w,
  foot-medium.jpg 320w,
  food-small.jpg 160w"
/>

Notice we’re not yet using the sizes attribute. Browsers will assume you’re probably going to render this image at 100vw wide. That’s unfortunate because the browser may download a larger image than it needs, which is what we’re trying to fight against with responsive images in the first place.

On my 2x desktop display, I can tell it’s downloading the 640px version, but it really only needs the 320px version.

It’s our CSS that controls how big these images render. In fact, we have a media query in this demo that says, “Hey browser, only render the images at 160px wide on Desktop displays.”

@media (min-width: 600px) {
  .slider img {
    width: 160px;
  }
}

You’d think the browser would know that, and it does — it just needs to download and parse the CSS first. The browser wants to make a decision about what to download faster than that. So, let’s tell it with the sizes attribute.

<img srcset="
  food-big.jpg 640w,
  foot-medium.jpg 320w,
  food-small.jpg 160w"
 
  sizes="(min-width: 600px) 160px, 320px"
/>

That’s saying, “OK, we’ll be rendering this image 160px wide on Desktop. Otherwise, let’s go with 320px wide.” With that in place, we can see the browser is making the right choice:

The browser is now only choosing to download the 320px version, which is correct on a 2x display where the images are being rendered at 160px.

And just to be sure, here’s a narrow viewport (like “mobile” would be):

We’re back to downloading the 640px version, which is correct, since at this viewport size, the sizes attribute is telling the browser we intend to render at 320px and we’re on a 2x display.

There is even more nuance here

I showed this demo to Eric “Sizes Master” Portis, who confirmed all this, but had some browser-specific things to add. I’ll summarize (but note the date of this blog post because these things tend to change):

  • Firefox does exactly as described above.
  • Chrome does too, except that it will always use the larger version if it has that version in cache. So, if it has a 640px version in cache, it knows it really only needs a 320px version, but since it doesn’t have a 320px version in cache, it’ll use the 640px version instead.
  • Safari does too, except that after it has made its choice, it never changes (like if you resize the browser window).
  • An interesting part about srcset is that the spec allows it to make choices however it wants, perhaps using stuff like network conditions to decide. Most browsers don’t do anything like this yet, except Chrome which downloads the smallest resource should it get a Save-Data header on the HTML document.
  • Joe McGill also noted: “For older iOS devices that don’t support w descriptors in srcset, the first source item in the list will be used, so you may want to lead with your preferred default size if you’re supporting legacy iOS devices.” In other words, those older iOS devices may have supported earlier syntaxes of responsive images, but only with x descriptor (like big-image.jpg 2x), so perhaps specify a good default image as the first in your srcset.

It might be helpful to look at Eric’s fork to more easily see what gets downloaded:

See the Pen Responsive Images Slider by Eric Portis (@eeeps) on CodePen.

Random notes

  • I snagged those food photos from Unsplash.
  • I uploaded them to Cloudinary so that I could use URL params to resize them instead of having to deal with that myself.
  • They are all squares, not because I downloaded them like that, but because I sized them that way in CSS and prevented squishing with object-fit. That means image data is being downloaded that isn’t needed, so I either should have cropped them myself or used Cloudinary URL params to do that.
  • I used the Pug HTML preprocessor for my demo just to reduce code repetition.

This is easier to look at:

img(srcset=`
  ${img_base}w_640${img_1} 640w,
  ${img_base}w_320${img_1} 320w,
  ${img_base}w_160${img_1} 160w,
` sizes=sizes)

…than the output:

<img srcset="
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_640/v1531407936/robin-stickel-82145-unsplash_qnexwz.jpg 640w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_320/v1531407936/robin-stickel-82145-unsplash_qnexwz.jpg 320w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_160/v1531407936/robin-stickel-82145-unsplash_qnexwz.jpg 160w,
  " sizes="(min-width: 600px) 160px, 320px"/><img srcset="
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_640/v1531407936/cel-lisboa-60315-unsplash_qsji9j.jpg 640w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_320/v1531407936/cel-lisboa-60315-unsplash_qsji9j.jpg 320w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_160/v1531407936/cel-lisboa-60315-unsplash_qsji9j.jpg.jpg 160w,
  " sizes="(min-width: 600px) 160px, 320px"/><img srcset="
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_640/v1531407936/charles-deluvio-466056-unsplash_ocd3dh.jpg 640w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_320/v1531407936/charles-deluvio-466056-unsplash_ocd3dh.jpg 320w,
    https://res.cloudinary.com/css-tricks/image/upload/c_scale,f_auto,q_auto,w_160/v1531407936/charles-deluvio-466056-unsplash_ocd3dh.jpg.jpg 160w,
  " sizes="(min-width: 600px) 160px, 320px"/>

Sometimes `sizes` is quite important. originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/sometimes-sizes-is-quite-important/feed/ 7 273936
An Almost Ideal React Image Component https://css-tricks.com/an-almost-ideal-react-image-component/ Thu, 21 Jun 2018 19:48:15 +0000 http://css-tricks.com/?p=272907 Yes, this is a React component, but regardless if you care about that part or not, the “ideal image component” part could be of interest. There is a lot to consider with how we put images on web pages these …


An Almost Ideal React Image Component originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Yes, this is a React component, but regardless if you care about that part or not, the “ideal image component” part could be of interest. There is a lot to consider with how we put images on web pages these days. This deals with:

  • Placeholder space (and then flexible responsive styles after loading)
  • Low-quality placeholder images
  • Responsive images syntax (srcset)
  • Image formats (e.g. using WebP when you can)
  • Click-to-load on bad network connections
  • Better UX for loading errors, with translatable copy

That’s not even all of it. So much to think about with poor little <img>. I enjoyed Alejandro Sanchez’s response:

To Shared LinkPermalink on CSS-Tricks


An Almost Ideal React Image Component originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
272907
The Four Big Ways Jetpack Helps with Image Performance https://css-tricks.com/the-four-big-ways-jetpack-helps-with-image-performance/ https://css-tricks.com/the-four-big-ways-jetpack-helps-with-image-performance/#comments Tue, 19 Jun 2018 15:18:57 +0000 http://css-tricks.com/?p=272663 (This is a sponsored post.)

We’ve been working with Jetpack around here as a sponsor. It’s a great match because as someone with a bunch of self-hosted WordPress sites, Jetpack is one of those no-brainer plugins for me. Jetpack …


The Four Big Ways Jetpack Helps with Image Performance originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
(This is a sponsored post.)

We’ve been working with Jetpack around here as a sponsor. It’s a great match because as someone with a bunch of self-hosted WordPress sites, Jetpack is one of those no-brainer plugins for me. Jetpack can do a ton of good things for any site in a variety of very different ways. Here’s one way to think about it: it brings the power of WordPress’ own massive servers to you.

For now, let’s just focus on one angle of what Jetpack can do for you: image performance. Jetpack does a ton for you in this regard, solving some non-trivial performance upgrades. Let’s take a look at what I see as the four big boosts you get from Jetpack on your images.

1) WordPress does responsive images for you

OK, I cheated with the first one because you don’t actually need Jetpack to benefit from this. But it’s an important and foundational concept for fast images. Just by using WordPress, you get basic responsive images for free.

If you already know what I’m talking about, here’s an example of the output you’ll see in the DOM of a published WordPress post with an image in it uploaded via the Media Uploader:

It’s wonderful to get this for free, as writing out responsive images syntax by hand is quite cumbersome.

If you are new to the idea of responsive images, the big idea is this: rather than a single image going to any browser visiting your website, you have multiple images in different sizes and the most-correct one is delivered. Imagine instead of a mobile phone downloading a 1600 pixel wide image (way bigger than it needs), it only downloads a 320-pixel wide image, saving a ton of downloading time.

We’ve written lots about responsive images over the years.

2) You get a CDN

Read a bit about web performance and you’ll be unanimously told: “use a CDN.” A CDN is a Content Delivery Network, essentially web servers designed specifically to make serving assets like images super fast. They call it a network because it isn’t just one server, it’s many servers physically located all over the world so that when your website is requested from different locations all around the world, the files being sent back come from geographically closer locations (faster!). Not to mention it does other clever things like not requiring cookies for each web request like your own server probably does.

Literally, flip a switch in Jetpack and you’ll be using an image CDN:

It’s called Photon.

Site speed is impacted by many factors and one of them is content delivery. Using what is referred to as a content delivery network (or CDN) helps by:

  • Delivering your content from high-speed and dedicated data centers.
  • More files can be downloaded simultaneously by the browser.
  • Distributed data centers (ie in different geographic locations) improve download speeds and provide redundancy.
  • By distributing load and save bandwidth you reduce your existing hosting costs (or keep them in check).

3) You get optimization

Una Kravets calls image optimization an easy performance win for designers. It’s an easy thing to see. Try taking a screenshot of something, exporting something from Photoshop, or grabbing some stock photography. Then drop it onto a tool like ImageOptim and watch the bytes fall away as it optimizes it. Massive savings.

But wouldn’t it be nice if it wasn’t on you to manually optimize all your images before using them? Computers are supposed to help us with menial tasks, right?! When you flip on the CDN feature of Jetpack, your images are now hosted on Photon, and you can see in the Photon docs how it handles things like resizing and quality for you.

4) You get lazy loading

Lazy loading is the idea that you don’t load anything at all unless you need it. In the case of images, don’t download the image unless it’s visible on the page. As in, don’t download an image that is three quarter down an article that a user might never scroll down to, but if they do, then download it.

You know what they say, the fastest web request is one that is never made. Jeremy Wagner, for Google, says:

When we lazy load images and video, we reduce initial page load time, initial page weight, and system resource usage, all of which have positive impacts on performance.

This is another flip-a-switch feature that works on any theme. Turn it on, you got lazy loading.

All Together Now

  1. You get responsive images with WordPress, which by itself can be a major performance win.
  2. With Jetpack, those responsive images are CDN-hosted, providing a speed boost and great caching for the images that are downloaded.
  3. Just because you’re using responsive images and a CDN doesn’t automatically mean those images are optimized, but they are on Photon.
  4. Last, nothing is downloaded at all unless the images are in view (lazy loading), which is the most efficient thing you can do.

Pretty compelling.


The Four Big Ways Jetpack Helps with Image Performance originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/the-four-big-ways-jetpack-helps-with-image-performance/feed/ 3 272663
w descriptors and sizes: Under the hood https://css-tricks.com/w-descriptors-and-sizes-under-the-hood/ Sun, 01 Apr 2018 13:23:47 +0000 http://css-tricks.com/?p=269127 Eric Portis digs into how the browser decides which image to downloads when you give it <img srcset="" sizes"">. Notably, a browser can do whatever it wants:

Intentionally un-specified behavior lets browsers provide innovative answers to an open-ended question.


w descriptors and sizes: Under the hood originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Eric Portis digs into how the browser decides which image to downloads when you give it <img srcset="" sizes"">. Notably, a browser can do whatever it wants:

Intentionally un-specified behavior lets browsers provide innovative answers to an open-ended question.

Still, calculations happen based on what you give it and you can still do a good job with that part.

The very weirdest part about all this is that the sizes attribute can alter an images “natural width”, which can lead to unexpected upscaling, a thing we’re trained to loathe. If you’re in that situation, you can either…

  • use an inline width attribute
  • set an inline style with a max-width
  • embrace the fact you might experience occassinal upscaling

To Shared LinkPermalink on CSS-Tricks


w descriptors and sizes: Under the hood originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
269127
Fallbacks for Videos-as-Images https://css-tricks.com/fallbacks-videos-images/ https://css-tricks.com/fallbacks-videos-images/#comments Wed, 07 Feb 2018 21:25:42 +0000 http://css-tricks.com/?p=266300 Safari 11.1 shipped a strange-but-very-useful feature: the ability to use a video source in the <img> tag. The idea is it does the same job as a GIF (silent, autoplaying, repeating), but with big performance gains. How big? “20x faster


Fallbacks for Videos-as-Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Safari 11.1 shipped a strange-but-very-useful feature: the ability to use a video source in the <img> tag. The idea is it does the same job as a GIF (silent, autoplaying, repeating), but with big performance gains. How big? “20x faster and decode 7x faster than the GIF equivalent,” says Colin Bendell.

Not all browsers support this so, to do a fallback, the <picture> element is ready. Bruce Lawson shows how easy it can be:

<picture>
  <source type="video/mp4" srcset="adorable-cat.mp4">
  <!-- perhaps even an animated WebP fallback here as well -->
  <img src="adorable-cat.gif" alt="adorable cat tears throat out of owner and eats his eyeballs">
</picture>

Šime Vidas notes you get wider browser support by using the <video> tag:

<video src="https://media.giphy.com/media/klIaoXlnH9TMY/giphy.mp4" muted autoplay loop playsinline></video>

But as Bendell noted, the performance benefits aren’t there with video, notably the fact that video isn’t helped out by the preloader. Sadly, <video> it is for now, as:

there is this nasty WebKit bug in Safari that causes the preloader to download the first <source> regardless of the mimetype declaration. The main DOM loader realizes the error and selects the correct one. However, the damage will be done. The preloader squanders its opportunity to download the image early and on top of that, downloads the wrong version wasting bytes. The good news is that I’ve patched this bug and it should land in Safari TP 45.

In short, using the <picture> and <source type> for mime-type selection is not advisable until the next version of Safari reaches the 90%+ of the user base.

Still, eventually, it’ll be quite useful.


Fallbacks for Videos-as-Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/fallbacks-videos-images/feed/ 11 266300
#155: Responsive Images, WordPress, and Cloudinary https://css-tricks.com/videos/155-responsive-images-wordpress-cloudinary/ Thu, 22 Jun 2017 17:51:16 +0000 http://css-tricks.com/?page_id=256028 Eric Portis joins me to dig into the world of responsive images.

We start at the basics. Responsive images are specifically images in HTML and exist because of a desire for better performance. Images are probably the biggest culprit in …


#155: Responsive Images, WordPress, and Cloudinary originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Eric Portis joins me to dig into the world of responsive images.

We start at the basics. Responsive images are specifically images in HTML and exist because of a desire for better performance. Images are probably the biggest culprit in the overall weight of websites. If we can avoid sending too many pixels across the network, we should. After all, a screen that is only 720 pixels wide doesn’t need a 2000 pixel wide image, even if it’s a 2x display.

The trouble is, browsers are really aggressive about downloading assets like images. That’s good, as it’s why the web (can be) as fast as it is. But is also means that we need to provide a bunch of information about our images right in the HTML. Can’t a browser just know how big an image is? Sure it can, after it has downloaded it. Can’t a browser know how big an image is going to be shown on the page? Sure it can, after it’s downloaded all the CSS and performed layout. The responsive images syntax tries to get ahead of all that by providing that information right in the syntax. Figuring out that syntax is tricky! There is srcset, sizes, and the <picture> element, and there is a ton to wrap your mind around there.

It gets more complicated still.

The syntax you need to build is based around having multiple copies of that image in which to build the syntax around. How do you make them? How many should you make? What size should they be?

Fortunately, there is some automated tooling popping up around responsive images. WordPress was an early player. Out of the box, WordPress will create multiple versions of the images you upload and output <img> tags with a helpful srcset syntax. But you can do much better. You can provide a sizes attribute that actually matches what your theme is doing and how it’s sizing those images.

Also, WordPress doesn’t use any special intelligence for creating multiple copies of the images you upload. But, it could. A tool like the responsive image breakpoint generator does use some intelligence for figuring out how many different images you can make, so you can strike a balance between having close to the perfect image for the job and not needing to make hundreds or thousands of copies of it. That tool has an API!

It gets more complicated still.

Different browsers support different image formats. For example, WebP. There are performance savings to be had by serving the right image format to the right browser. The responsive images syntax can help with that, but it complicates the syntax. Many image formats have a notion of quality as well. What quality do you save these multiple copies at?

At this point, it’s a good time to mention Cloudinary. I’ve got it integrated right now on CSS-Tricks, and it helps with a lot of this stuff. I should mention that I’m a paying Cloudinary customer, and this screencast was not sponsored, but Cloudinary has sponsored CSS-Tricks in the form of two highly related sponsored posts:

  1. Responsive Images in WordPress with Cloudinary, Part 1
  2. Responsive Images in WordPress with Cloudinary, Part 2

In a nutshell, here’s how it all works on CSS-Tricks now:

  1. I upload images just like I always would with WordPress.
  2. Rather than the srcset information being generated with WordPress, it’s generated by this smarter API.
  3. The image is also uploaded to Cloudinary.
  4. When WordPress filters and outputs the HTML for the images, all that good srcset (and custom sizes) data is applied. The URL’s point to Cloudinary URLs.
  5. The Cloudinary URL’s make use of Cloudinary’s ability to automatically adjust both the format and the quality automatically (using their fancy technology) to make sure things are the best they can be for the particular browser requesting the image.
  6. Old images that weren’t uploaded to Cloudinary originally still benefit from all the Cloudinary goodness. They don’t have as smart of srcset data, but they still use “fetch” URL’s meaning they can benefit from auto formatting and auto quality (which is probably a decent bit of the performance improvement, anyway).

In short, not only am I using responsive images here on CSS-Tricks to help with performance, the Cloudinary integration seriously ups that game.

I’m also happy that this isn’t a hard dependency. If the Cloudinary plugin is ever shut off, everything just goes back to normal WordPress responsive images.


#155: Responsive Images, WordPress, and Cloudinary originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
256028
Responsive Images in WordPress with Cloudinary, Part 2 https://css-tricks.com/responsive-images-wordpress-cloudinary-part-2/ https://css-tricks.com/responsive-images-wordpress-cloudinary-part-2/#comments Mon, 27 Feb 2017 12:37:53 +0000 http://css-tricks.com/?p=251741 (This is a sponsored post.)

In Part 1 of this series, I provided some background on responsive images, describing how you can add srcset and sizes attributes to an img element to serve appropriately sized image files to users …


Responsive Images in WordPress with Cloudinary, Part 2 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
(This is a sponsored post.)

In Part 1 of this series, I provided some background on responsive images, describing how you can add srcset and sizes attributes to an img element to serve appropriately sized image files to users of a website based on the size and capabilities of their browser and device. I also shared how WordPress used its native image resizing functionality to implement srcset and sizes automatically and how you can use an external image service like Cloudinary to extend the native implementation that WordPress provides.

Article Series:

  1. An Intro to Responsive Images and WordPress
  2. A WordPress Plugin integrating Cloudinary and Responsive Images (you are here!)

In this installment, I go into more detail about how image resizing in WordPress works. I explain how I used built-in WordPress hooks along with Cloudinary’s application programming interface (API)—and its PHP integration library—to create a WordPress plug‑in that offloads responsive image resizing and optimization to Cloudinary.

Getting Started

This article assumes that you understand how to create a WordPress plug‑in. If you don’t, read the WordPress Codex article Writing a Plugin before continuing. For my example, I used the WP-CLI to create a scaffold for my plug‑in from the command line.

Next, follow the instructions in the WordPress PHP Getting Started Guide to download and include Cloudinary’s PHP library in your plug‑in. Save the library files to the `/lib/cloudinary_php/` directory within your plug‑in, and include them in your main plug‑in file with the following code:

// Load dependencies.
require 'lib/cloudinary_php/src/Cloudinary.php';
require 'lib/cloudinary_php/src/Uploader.php';
require 'lib/cloudinary_php/src/Api.php';

Finally, set up Cloudinary using the configuration parameters found in your Cloudinary management console, which you define as constants in your `wp-config.php` file because it’s a bad idea to save account information directly to your plug‑in. Here’s what the config setup in my plug‑in looks like:

Cloudinary::config( array(
 "cloud_name" => CLD_CLOUD_NAME,
 "api_key"    => CLD_API_KEY,
 "api_secret" => CLD_API_SECRET
) );

Now that you have configured your plug‑in to communicate with the Cloudinary API, you can start building the functionality of your plug‑in.

Image Resizing in WordPress

In planning my plug‑in, I wanted to integrate seamlessly with the default user experience for managing images in WordPress. I also wanted to maintain local copies of my files so that everything would continue working even if I decided to deactivate the plug‑in. To fulfill these goals, it’s helpful to understand what WordPress does whenever you upload an image.

By default, wp_generate_attachment_metadata() resizes images and stores metadata about them in the database.

When you upload an image, WordPress executes the media_handle_upload() function, which saves the uploaded file to the server and creates a new post in the database representing the image using the wp_insert_attachment() function. After the post is created, media_handle_upload() calls wp_generate_attachment_metadata() to create additional metadata about the image. It’s during this step that WordPress creates additional sizes of the image and includes information about those sizes in the attachment metadata. Here’s an example of what attachment metadata in WordPress looks like by default:

array(
  'width' => 1500,
  'height' => 1500,
  'file' => '2016/11/image.jpg',
  'sizes' => array(
    'thumbnail' => array(
      'file' => 'image-150x150.jpg',
      'width' => 150,
      'height' => 150,
      'mime-type' => 'image/jpeg',
    ),
    'medium' => array(
      'file' => 'image-300x300.jpg',
      'width' => 300,
      'height' => 300,
      'mime-type' => 'image/jpeg',
    ),
    'large' => array(
      'file' => 'image-1024x1024.jpg',
      'width' => 1024,
      'height' => 1024,
      'mime-type' => 'image/jpeg',
    ),
  ),
)

As you can see, WordPress now knows the file name and dimensions for the original image file, and for any additional sizes created by WordPress after the image was uploaded. WordPress references this metadata to create the HTML markup needed to display the image on a webpage. This is the information we will extend by including information generated by the Cloudinary API.

Integrating WordPress with Cloudinary

For the remainder of this article, I’ll be referencing the Cloudinary_WP_Integration class in the plug‑in. Here’s the full source code for this class. In this class, I’ve added a method named register_hooks(), which adds all my custom functionality by taking advantage of WordPress built-in filter hooks. To better understand WordPress hooks, read about the Plugin API in the WordPress Codex.

generate_cloudinary_data() mirrors images to Cloudinary and saves additional, Cloudinary-specific data to the database.

Because I want to mirror uploaded files with Cloudinary and use its API to generate a set of image sizes for use in srcset attributes, the first thing I do is hook into the wp_generate_attachment_metadata filter to extend the metadata that WordPress is already creating. To register this functionality, I added the following code to my register_hooks() class:

add_filter( 'wp_generate_attachment_metadata', array( $this, 'generate_cloudinary_data' ) );

This tells WordPress to call the generate_cloudinary_data() method in my class when the wp_generate_attachment_metadata filter is fired. Here’s what that method looks like:

public function generate_cloudinary_data( $metadata ) {
 // Bail early if we don't have a file path to work with.
 if ( ! isset( $metadata['file'] ) ) {
  return $metadata;
 }

 $uploads = wp_get_upload_dir();
 $filepath = trailingslashit( $uploads['basedir'] ) . $metadata['file'];

 // Mirror the image on Cloudinary and build custom metadata from the response.
 if ( $data = $this->handle_upload( $filepath ) ) {
  $metadata['cloudinary_data'] = array(
   'public_id'  => $data['public_id'],
   'width'      => $data['width'],
   'height'     => $data['height'],
   'bytes'      => $data['bytes'],
   'url'        => $data['url'],
   'secure_url' => $data['secure_url'],
  );

  foreach ( $data['responsive_breakpoints'][0]['breakpoints'] as $size ) {
   $metadata['cloudinary_data']['sizes'][$size['width'] ] = $size;
  }
 };

 return $metadata;
}

This code uses wp_get_upload_dir() to build the path to the uploaded image and passes it to a second method, handle_upload(), which uploads the image to Cloudinary and returns data from the API. When handle_upload() is complete, I add the returned data to a cloudinary_data array in the metadata, and then loop through each breakpoint size that Cloudinary returns—which I’ll explain in a moment—and save those to the cloudinary_data['sizes'] key. Let’s look at what happens in the handle_upload() method:

public function handle_upload( $file ) {
  $data = false;
  if ( is_callable( array( '\Cloudinary\Uploader', 'upload' ) ) ) {
    $api_args = array(
      'responsive_breakpoints' => array(
        array(
          'create_derived' => false,
          'bytes_step'  => 20000,
          'min_width' => 200,
          'max_width' => 1000,
          'max_images' => 20,
        ),
     ),
     'use_filename' => true,
    );
    $response = \Cloudinary\Uploader::upload( $file, $api_args );
    // Check for a valid response before returning Cloudinary data.
    $data = isset( $response['public_id'] ) ? $response : false;
  }
  return $data;
}

This method determines whether it can call the upload() method in the \Cloudinary\Uploader class I imported earlier by using is_callable(). If it can, it builds the arguments I plan to pass to \Cloudinary\Uploader::upload(). First, I use Cloudinary’s responsive image breakpoint functionality to automatically generate the best set of image sizes based on the content of the image itself. I’m passing a few options to the responsive_breakpoints argument here, so let me explain each:

  • create_derived tells Cloudinary whether it should create additional image files as soon as the original is uploaded. Passing false generates data about the image without actually creating the files until they’re requested.
  • bytes_step defines how many bytes should be allowed between images before creating a new size. I’m going with 20,000 (or 20 KB), but you can tweak that number to suit your needs.
  • The min_width and max_width arguments tell Cloudinary what the dimensions of the smallest and largest images, respectively, should be so that you don’t create unnecessary image sizes.
  • max_images sets the maximum total number of images that Cloudinary should create.

With this information, Cloudinary automatically determines the optimal number and size of images to create for use in srcset attributes. Finally, I set use_filename to true, which tells Cloudinary to use file names matching the one I’m uploading—defined as the $file variable—rather than generating random image file names. This helps me identify images in my Cloudinary library but makes no real difference otherwise.

Now that I have a way to automatically upload images to Cloudinary and save the returned data to the attachment metadata for my image, I can use these data to serve images from the Cloudinary content delivery network (CDN) rather than my local server. To do this, I first want to filter all attachment URLs so that the Cloudinary URL is used instead of local URLs. For this, I’ve added a filter named get_attachment_url() to the wp_get_attachment_url hook in my register_hooks() method here:

add_filter( 'wp_get_attachment_url', array( $this, 'get_attachment_url' ), 10, 2 );

This line returns the URL and attachment ID of an image to be passed to my get_attachment_url() method, which looks like this:

public function get_attachment_url( $url, $attachment_id ) {
  $metadata = wp_get_attachment_metadata( $attachment_id );

  if ( isset( $metadata['cloudinary_data']['secure_url'] ) ) {
    $url = $metadata['cloudinary_data']['secure_url'];
  }

  return $url;
}

This method looks up the metadata associated with my image and determines whether a URL from the cloudinary_data I saved in my last step exists. If it does, it returns the URL from Cloudinary. Otherwise, it returns the local URL.

This takes care of the URL for the full-sized image, but replacing URLs for any of the sizes WordPress creates (i.e., intermediate sizes) can be a bit trickier. To accomplish this, I need to hook into image_downsize(), which is the function WordPress uses to get information about intermediate sizes associated with an image. Here, I use Cloudinary instead of local files.

The following code registers my filter followed by the method that replaces the data from WordPress with data from Cloudinary:

add_filter( 'image_downsize', array( $this, 'image_downsize' ), 10, 3 );
public function image_downsize( $downsize, $attachment_id, $size ) {
  $metadata = wp_get_attachment_metadata( $attachment_id );

  if ( isset( $metadata['cloudinary_data']['secure_url'] ) ) {
    $sizes = $this->get_wordpress_image_size_data( $size );

    // If we found size data, let's figure out our own downsize attributes.
    if ( is_string( $size ) && isset( $sizes[ $size ] ) &&
       ( $sizes[ $size ]['width'] <= $metadata['cloudinary_data']['width'] ) &&
       ( $sizes[ $size ]['height'] <= $metadata['cloudinary_data']['height'] ) ) {

      $width = $sizes[ $size ]['width'];
      $height = $sizes[ $size ]['height'];

      $dims = image_resize_dimensions( $metadata['width'], $metadata['height'], $sizes[ $size ]['width'], $sizes[ $size ]['height'], $sizes[ $size ]['crop'] );

      if ( $dims ) {
        $width = $dims[4];
        $height = $dims[5];
      }

      $crop = ( $sizes[ $size ]['crop'] ) ? 'c_lfill' : 'c_limit';

      $url_params = "w_$width,h_$height,$crop";

      $downsize = array(
        str_replace( '/image/upload', '/image/upload/' . $url_params, $metadata['cloudinary_data']['secure_url'] ),
        $width,
        $height,
        true,
      );

    } elseif ( is_array( $size ) ) {
      $downsize = array(
        str_replace( '/image/upload', "/image/upload/w_$size[0],h_$size[1],c_limit", $metadata['cloudinary_data']['secure_url'] ),
        $size[0],
        $size[1],
        true,
      );
    }
  }

  return $downsize;
}

This is a long block of code, so let me walk through it. Again, I start by getting the attachment metadata and checking for the $metadata['cloudinary_data'] information. I then use a helper function called get_wordpress_image_size_data() to get the image sizes that are registered with WordPress, which I then pass to image_resize_dimensions() to calculate the expected dimensions if I’m using a named size (e.g., thumbnail, medium). If the $size parameter is already an array of dimensions, which happens occasionally, I pass those dimensions directly to Cloudinary for processing.

I should note here that I could have used the Cloudinary API to replicate all the alternate sizes WordPress creates. Instead, I chose to take advantage of Cloudinary’s dynamic URL image generation functionality to generate the additional sizes I need by replacing the URL to the full-sized image with dynamic parameters like this:

str_replace( 
  '/image/upload',
  "/image/upload/w_$size[0],h_$size[1],c_limit",
  $metadata['cloudinary_data']['secure_url'] );

If the image dimensions should be an exact crop, I’ll use Cloudinary’s c_lfill cropping algorithm. Otherwise, c_limit make images that fit within my target dimensions while maintaining the original file’s aspect ratio.

Once I’ve completed these steps, Cloudinary should serve any image newly uploaded to WordPress. The last task is to generate srcset and sizes attributes by using the metadata that I previously got back from Cloudinary’s responsive image breakpoint functionality.

Automatically Generating srcset and sizes

The payoff.

To understand the details of WordPress’s responsive images implementation, you may want to read Responsive Images in WordPress 4.4. To summarize, I’ll review the two occasions when WordPress dynamically adds srcset and sizes to images.

Responsive Markup for Dynamically Generated Images

First, WordPress automatically attempts to add these attributes to any image dynamically generated in a template using wp_get_attachment_image() or similar functions. You can add srcset and sizes attributes to these images by filtering the image attributes before the markup is assembled by using the wp_get_attachment_image_attributes filter:

add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 10, 3 );
public function wp_get_attachment_image_attributes( $attr, $attachment, $size ) {
  $metadata = wp_get_attachment_metadata( $attachment->ID );

  if ( is_string( $size ) ) {
    if ( 'full' === $size ) {
      $width = $attachment['width'];
      $height = $attachment['height'];
    } elseif ( $data = $this->get_wordpress_image_size_data( $size ) ) {
      // Bail early if this is a cropped image size.
      if ( $data[$size]['crop'] ) {
        return $attr;
      }

      $width = $data[$size]['width'];
      $height = $data[$size]['height'];
    }
  } elseif ( is_array( $size ) ) {
    list( $width, $height ) = $size;
  }

  if ( isset( $metadata['cloudinary_data']['sizes'] ) ) {
    $srcset = '';

    foreach( $metadata['cloudinary_data']['sizes'] as $s ) {
      $srcset .= $s['secure_url'] . ' ' . $s['width'] . 'w, ';
    }

    if ( ! empty( $srcset ) ) {
      $attr['srcset'] = rtrim( $srcset, ', ' );
      $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width );

      // Convert named size to dimension array for the filter.
      $size = array($width, $height);
      $attr['sizes'] = apply_filters( 'wp_calculate_image_sizes', $sizes, $size, $attr['src'], $metadata, $attachment->ID );
    }
  }

  return $attr;
}

In the wp_get_attachment_image_attributes() method, you calculate the dimensions of the image based on the $size parameter. For now, I’m only adding srcset and sizes to those images with an aspect ratio matching the original file so that I can take advantage of the breakpoint sizes Cloudinary provided when I uploaded my image. If I determine that the $size is a different aspect ratio (e.g., a hard crop), I return the $attr value unchanged.

Once you have the dimensions of your image, you loop through all the breakpoint sizes from the $metadata['cloudinary_data']['sizes'] array to build the srcset attribute. Afterward, you create a sizes attribute based on the width of the image. Finally, you pass your sizes attribute value to the wp_calculate_image_sizes() filter so that themes and plug‑ins can modify the sizes attribute based on their specific layout needs.

Responsive Markup for Images in Post Content

WordPress also automatically adds srcset and sizes attributes to images embedded in post content. Instead of saving these attributes in the post content in the database, WordPress generates them dynamically when the page is generated. That way, as new methods for serving responsive images become available, WordPress can easily adopt them.

You want your Cloudinary integration to be just as future friendly as the native implementation. So, replace the content filter that WordPress uses — wp_make_content_images_responsive() — with your own filter, named make_content_images_responsive(). Here’s the code that accomplishes both tasks:

// Replace the default WordPress content filter with our own.
remove_filter( 'the_content', 'wp_make_content_images_responsive' );
add_filter( 'the_content', array( $this, 'make_content_images_responsive',  ) );
public function make_content_images_responsive( $content ) {
  if ( ! preg_match_all( '/<img [^>]+>/', $content, $matches ) ) {
    return $content;
  }

  $selected_images = $attachment_ids = array();

  foreach( $matches[0] as $image ) {
    if ( false === strpos( $image, ' srcset=' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) &&
      ( $attachment_id = absint( $class_id[1] ) ) ) {

      /*
       * If exactly the same image tag is used more than once, overwrite it.
       * All identical tags will be replaced later with 'str_replace()'.
       */
      $selected_images[ $image ] = $attachment_id;
      // Overwrite the ID when the same image is included more than once.
      $attachment_ids[ $attachment_id ] = true;
    }
  }

  if ( count( $attachment_ids ) > 1 ) {
    /*
     * Warm object cache for use with 'get_post_meta()'.
     *
     * To avoid making a database call for each image, a single query
     * warms the object cache with the meta information for all images.
     */
    update_meta_cache( 'post', array_keys( $attachment_ids ) );
  }

  foreach ( $selected_images as $image => $attachment_id ) {
    $image_meta = wp_get_attachment_metadata( $attachment_id );
    $content = str_replace( $image, $this->add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content );
  }

  return $content;
}

The make_content_images_responsive() method is essentially a copy of the wp_make_content_images_responsive() function from WordPress, which searches the content for all <img> elements — handling some edge cases and including some performance optimizations in the process—and passes them to a second function that handles adding the srcset and sizes attributes. I created a custom callback method in my class named add_srcset_and_sizes() for this purpose:

public function add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
  if ( isset( $image_meta['cloudinary_data']['sizes'] ) ) {
    // See if our filename is in the URL string.
    if ( false !== strpos( $image, wp_basename( $image_meta['cloudinary_data']['url'] ) ) && false === strpos( $image, 'c_lfill') ) {
      $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : '';
      $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : 0;
      $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;

      $srcset = '';

      foreach( $image_meta['cloudinary_data']['sizes'] as $s ) {
        $srcset .= $s['secure_url'] . ' ' . $s['width'] .  'w, ';
      }

      if ( ! empty( $srcset ) ) {
        $srcset = rtrim( $srcset, ', ' );
        $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width );

        // Convert named size to dimension array.
        $size = array($width, $height);
        $sizes = apply_filters( 'wp_calculate_image_sizes', $sizes, $size, $src, $image_meta, $attachment_id );
      }

      $image = preg_replace( '/src="([^"]+)"/', 'src="$1" srcset="' . $srcset . '" sizes="' . $sizes .'"', $image );
    }
  }

  return $image;
}

Here, I again make sure that my attachment metadata includes size data from Cloudinary. Then, I make sure that the image markup includes the same file name as the image I uploaded to Cloudinary, just in case the image markup hasn’t been edited after it was inserted into the content. Finally, I include false === strpos( $image, 'c_lfill') to determine whether the URL indicates that Cloudinary is hard-cropping the image, similar to how I checked for hard cropping in wp_get_attachment_image_attributes(). If all checks pass, I can loop through the breakpoint sizes that were created when I originally uploaded the image to Cloudinary and use those data to build out my srcset and sizes attributes.

With this functionality, you can now successfully offload all your responsive image processing to Cloudinary and serve optimized images from the Cloudinary CDN instead of your local web server.

Wrap‑Up

I hope this gives you a better understanding of how WordPress handles resizing images and shows how you can extend WordPress to take advantage of Cloudinary to dynamically generate and serve images that are optimized for different device types and sizes. To try this code out on your site, download the plug‑in from GitHub, and be sure to leave feedback about anything that you think could be improved.


This post (and the plugin!) was written by Joe McGill.

Article Series:

  1. An Intro to Responsive Images and WordPress
  2. A WordPress Plugin integrating Cloudinary and Responsive Images (you are here!)

Cloudinary can help with responsive images on the web in a big way! They not only can create the different sizes of images needed to send the best possible size, but they can send it in the right format and even completely automate the process, for any application.


Responsive Images in WordPress with Cloudinary, Part 2 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/responsive-images-wordpress-cloudinary-part-2/feed/ 1 251741