The problem with the display of pictures has arisen since the advent of adaptability on the Internet. We want the site to look good on any tablet, phone, in portrait or landscape orientation of the screen, as well as on super-large 5K displays. Retina displays with high pixel density (DPI) also appeared on the market, where ordinary pictures look blurry. The share of mobile traffic is growing, and large resources are aimed at economical image loading. Consider how to solve these problems on the sites of Apple, Tilda and the blog platform Medium.
Apple: background-image power user
Apple in its products is betting on design, paying great attention to the study of displaying pictures. On the Apple site - at the time of this writing - image optimization is provided by the capabilities of CSS, more precisely, background-image. The key principle of this approach is the fixed width of the picture between breakpoints, that is, the rejection of “rubber” pictures. Consider the example of one of the images from the site
www.apple.com/mac<figure class="imac-image" data-progressive-image=""></figure>
.section-imac .imac-image { width:939px; height:631px; background-size:939px 631px; background-repeat:no-repeat; background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_large.jpg"); } @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx), (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .section-imac .imac-image { background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_large_2x.jpg") } } @media only screen and (max-width: 1068px) { .section-imac .imac-image { width:795px; height:536px; background-size:795px 536px; background-repeat:no-repeat; background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_medium.jpg") } } @media only screen and (max-width: 1068px) and (-webkit-min-device-pixel-ratio: 1.5), only screen and (max-width: 1068px) and (min-resolution: 1.5dppx), only screen and (max-width: 1068px) and (min-resolution: 144dpi) { .section-imac .imac-image { background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_medium_2x.jpg") } } @media only screen and (max-width: 735px) { .section-imac .imac-image { width:365px; height:246px; background-size:365px 246px; background-repeat:no-repeat; background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_small.jpg") } } @media only screen and (max-width: 735px) and (-webkit-min-device-pixel-ratio: 1.5), only screen and (max-width: 735px) and (min-resolution: 1.5dppx), only screen and (max-width: 735px) and (min-resolution: 144dpi) { .section-imac .imac-image { background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_small_2x.jpg") } }
For cross-browser compatibility, three ways are used at once to set the pixel density of the screen:
-webkit-min-device-pixel-ratio: 1.5
min-resolution: 1.5dppx
min-resolution: 144dpi
')
What are the pros and cons of this approach?
pros
- Ease of implementation. This is perhaps the most understandable way to change the picture to a certain size and density of the screen.
- Coverage of all problems: the issue of screen size and pixel density is solved.
- No jumping layout, thanks to fixed in advance size.
- You can use the built-in CSS capabilities for working with backgrounds (blend-mode for effects, position - crop the image, if necessary).
Minuses
- You can not make a “rubber” picture. Fixed dimensions determine the aspect ratio, so when trying to compress or stretch, we cut the picture or appear an empty space at the bottom. Yes, you can use padding-bottom in percent instead of size, but this is a workaround, a CSS hack, which complicates the basic concept.
- Lots of CSS code. Yes, CSS is easy to read, but with a large number of breakpoints and images, the size of the CSS can increase to infinity (see below “CSS preprocessors rush to the rescue”).
- No control over the loading of images. If you use this approach in its pure form, then you will load all the pictures on the page at the same time. And CSS, unlike img, does not have onload callbacks that allow you to control whether images are loaded or not.
- Requires meticulous care. You will have to set the size for all the pictures. And last but not least, when replacing one image with another, you will need to edit CSS for new sizes.
CSS preprocessors rush to the rescue
Most likely, you already use one of the CSS preprocessors, such as SCSS, LESS, PostCSS or Stylus - and they can greatly simplify your life. Let's try to optimize the code from the Apple site using SCSS. For media expressions, take the mixin
media and write your mixin for the picture:
@mixin image($width, $height, $url) { width: $width; height: $height; background-size: $width $height; background-repeat:no-repeat; background-image:url($url); }
Set the variables:
$breakpoints: ( 'phone': 735px, 'tablet': 1068px ); $media-expressions: ( 'screen': 'only screen', 'retina': '(-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 1.5dppx), (min-resolution: 144dpi)' );
We get a SCSS that is compiled one to one in Apple's CSS:
.section-imac .imac-image { @include image(939px, 631px, "image_large.jpg"); } @include media('screen', 'retina') { .section-imac .imac-image { @include image(939px, 631px, "image_large2x.jpg"); } } @include media('screen', '<=tablet') { .section-imac .imac-image { @include image(795px, 536px, "image_medium.jpg"); } } @include media('screen', '<=tablet', 'retina') { .section-imac .imac-image { @include image(795px, 536px, "image_medium_2x.jpg"); } } @include media('screen', '<=phone') { .section-imac .imac-image { @include image(365px, 246px, "image_small.jpg"); } } @include media('screen', '<=phone', 'retina') { .section-imac .imac-image { @include image(365px, 246px, "image_small_2x.jpg"); } }
Summary
This is a great approach that you can live with if you don’t have many pictures or they rarely change.
Tilda: background-image with blur effect
A tilde is a website builder that provides different components for displaying images. Consider the easiest way to “lazy” loading images. What does html look like before loading the image:
<div data-original="https://static.tildacdn.com/image.jpg" style="background: rgba(0, 0, 0, 0) url(https://static.tildacdn.com/image-small.jpg) no-repeat scroll center center / cover;"> </div>


The effect of blurring is achieved due to the fact that the preview has a small size (20x20 pixels), but with the help of the css property cover the image is stretched to the full width and height. Minus: this blurry does not look very nice, but it is very simple and does not overload the processor.
After downloading, the image is simply substituted in full size in the inline style.
pros
- Lazy loading.
- Easy to maintain.
Minuses
- The same picture for any devices. Adaptability is realized only within the limits of stylization (the picture is simply cropped differently).
- Ugly blur effect.
- There is no transition between blur and full resolution.
Summary
This is a working method if you need a simple component and do not want to bother with six or more pictures for different screen resolutions. Good for sites with a large number of images.
Medium: canvas blur
The blog platform meduim.com produced a great deal of reason in the world of web images due to the beautiful blur effect for underloaded images. This effect is achieved using the stackblur-canvas library.


HTML looks like a sandwich:
<img src="placeholder.jpg"> <canvas> <img src="full-size.jpg">
This allows for a smooth transition effect. In a full-size picture, opacity: 0 initially through the css transition goes into opacity: 1.
Blur Battle: CSS vs SVG vs Canvas
So, if we are not Apple, it will most likely be more convenient to use lazy loading of pictures and a blur effect. At the moment there are at least three ways to create a blair effect (and another option from Tilda, but we will not consider it, since this is not a “real” blair).
Why is it still worth using stackblur-canvas for blur if you already have a built-in css browser filter for blur?
- Problems. CSS and SVG filters by default blur the edges of the image. For the svg filter there is a special solution , for the built-in filter: blur () you will have to use edge clipping.
- Performance. Stackblur-canvas works very fast, css filters work even faster, therefore, with a large number of blurred images, the choice is uniquely behind css filters.
- Beauty. Here it is subjective, but in my opinion stackblur looks more beautiful. The svg filter has ugly artifacts.
Picture tag - the pinnacle of evolution
At the very beginning, we described the Apple method and found out that in css there are media expressions for both screen size and pixel density. And they can be used simultaneously to select a specific image to display. The only drawback is that the picture on the background does not “know” its aspect ratio, it cannot be “rubber”. In addition, from the point of view of the semantics of the web, use the background incorrectly to display all the pictures in a row.
The img tag can have a srcset attribute, but you must select a screen size or pixel density — you cannot combine these parameters.
The picture tag is free from this flaw, and also, like img, “knows” its aspect ratio. Already, it is supported by
92% of browsers. This tag is suitable for those who can afford not to adapt to IE.
Intersection Observer API
Finally, let's talk about the implementation of lazy loading images. The classic approach is to use a combination of scroll, resize and orientationchange events and calculate the position of a particular image relative to the visible screen area.
In 2019, a set of crutches can be replaced with a special browser API -
intersection observer . It is supported by all modern browsers. For older iOS and IE, use the official
w3c polyfil .
Looking to the future: what else is missing for complete happiness
So, we already have a picture tag, which has good browser support, but the problem of lazy loading remains. We still need to rely on our own implementations based on intersection observer or a combination of JS events. When will browsers offer a native solution? For example, the Chrome 75 version promises to provide support for the loading property for img and iframe tags. You can update your components for native support of lazy loading by checking the presence of the property in the prototype of the picture: if successful, you can opt out of your own lazy loading code, since the browser will do everything for you!
if ('loading' in HTMLImageElement.prototype)
CMS and custom content
Which display method is best for user uploaded images? As a rule, it is worth doing everything as easy as possible without forcing him to download pictures separately for the phone, tablet and desktop. So, we have only one picture for all devices. For such a situation, the Tilda (for maximum simplicity) or Medium approach is perfect - if you want to make a real blur effect, perhaps replacing stackblur-canvas with standard css: blur () for better performance.
Results
There is still no single, “most correct” way to display images. If you need to support old browsers and IE, then your choice is background-image with media expressions and img for simple cases. For new projects it is more convenient to replace background-image with a picture. If you want to do beautiful lazy loading, try stackblur-canvas, and if performance is important to you, then css filters or stretched pictures on a tilde background.