How We Treat Images

Jiří Zmrhal
Life at Apollo Division
8 min readApr 15, 2020

Collection of problems and solutions for handling images on the web.

The images belong under the most heavy-sized media on the web. They are a visual representation of the information and can have a contentful, decorative, or assistive role.

To have an image on a website could be as easy as inserting <img src="your-image-url" /> to the page HTML. However, many challenges are trying to make your work harder. Images can be huge in size, load slow, look weird, have poor quality, display incorrectly and so on. And even if not, there can always be some space for improvement. If we want to deliver the great user experience with serving images for our end users, choosing the right approach might be a little bit more challenging.

I’ll describe problems you can face with images on the web, and solutions including optimizations we use that work well and make sense for us.

In case you are interested just in code, see the Responsive Images Code Generator section at the end.

Bandwidth and File Size

Average bytes by content type on the web (desktops)

The average transfer size of all images is more than 30% of the page weight, which makes images a critical target for optimization. When you visit a page with unoptimized images, it can be frustrating to wait until the page is loaded.

Compression

Images transferred over the web are much larger than it could be with the compression, the optimization process that can be both manual and automatic. By choosing the right compression type you can save hundreds of kilobytes. However, if you decrease the file size too much, the image starts losing its quality so it’s important to find the optimal ratio between the quality and size.

We use

- On the server (Node): imagemin
- On build time (Webpack): imagemin-webpack

New-gen Image Format

Images with standard formats, used on 99.8% of websites, are around 30% bigger in size compared to the modern format images. Web developers can create smaller images that take less time to download and are equal in quality.

We use

- imagemin-webp
- Render one more image source in *.webp format for each existing JPEG/PNG/GIF image.

Lazy Loading

Lazy loading of images on scroll

Every image is requested and downloaded on page load by default, which leads to slower loading time on pages containing multiple images (even slower when images are not compressed). In addition, there is a waste of bandwidth usage if the user won’t go through the whole page so he won’t even see the downloaded images hidden out of the browser viewport.

Lazy loading is a technique allowing the browser to request and download images (or other content) when the image enters its viewport. Simply said, download the image just at the moment (or slightly before) when it’s visible to the user.

We use

- JavaScript library: lazysizes

Accessibility

Putting a plain image to a page, when speaking of search engines, screen readers and people with various disabilities or in various circumstances, is just not enough since we need to ensure our images can be consumed both visually and textually and are meaningful.

Although there are 7 groups of images from the concept perspective, the basic implementation categories can be split into 3:

  • Contentful — representation of part of the content
  • Assistive — description for the functionality of the connected element
  • Decorative — images without any added information to the page content

Semantics

Using wrong elements to display images breaks the rule of conveying meaning over the presentation and does not bring clarity for browser and search engines. By using proper HTML elements you’ll improve your SEO and make your site more findable.

We use

-<img> for contentful images
-<svg> for (assistive) icons and logos
-background-image on <div>, <article>, <section>, or any other element used for wrapping, for decorative images
-<figure> and <figcaption> if needed

Attributes

Implementing certain images without text alternatives makes it impossible to read or transform to text for people using screen readers, speech input software or even search engines. On the other hand, the text alternative for decorative images is redundant.

We use

-<img> with alt attribute containing image description and possibly aria-describedby attribute containing long description for contentful images
-<svg> with <title> element containing image description and possibly <desc> element containing long description for assistive images

Disabled JavaScript

JavaScript can fail. JavaScript can break. Some browsers turn it off on slow connections. Corporate firewalls might block it. There are many reasons to optimize the site to work without JavaScript. Since we lazy-load our images, we rely on it (at least for now). But we also provide an alternative what, of course, does not support lazy loading.

We use

-<noscript> containing the image with image sources directly in src attribute without lazy loading functionality

Responsivity

Displaying just one variant of an image for different screen sizes makes the image too small or too big for those screens. Serving large images on mobiles, used for example for the desktop banner, makes a mobile browser download the much larger image it needs much longer and vice versa.

The responsive technique is to prepare a few image variants, each with different sizes and each for different screen sizes. By serving those you can save bandwidth, improve user experience and let the decision what image to download up to the browser.

We use

-<img> with src, srcset and sizes attributes

High-Density Screens

Difference between CSS pixels and Device pixels

Displaying just one variant of an image for a specific screen size makes the image poor in quality on high-density screens. Most of the devices, especially mobile ones, have very high hardware resolution but still the small screen size. To understand the problem behind this, let’s dive deeper.

There are 2 types of resolution:

  • Hardware resolution — measured in device pixels — is a physical resolution of the device
  • CSS resolution — measured in CSS pixels — is a browser resolution

Let’s take Apple iPhone 8 as an example:

  • Hardware resolution: 750x1334px
  • CSS resolution: 375x667px

Here, the hardware resolution is 2x higher than the CSS one. The number 2 represents something called the device pixel ratio, which is a ratio between the device and CSS pixels of the device. So, speaking of Apple iPhone 8:

  • Device pixel ratio: 2
  • 1 CSS pixel = 2 device pixels

The problem is coming.

  1. The browser displays images in the CSS resolution, not the hardware resolution.
  2. As we already know, 1 CSS pixel = 2 device pixels.
  3. We want to display an image with resolution of 100x100px, so 100 CSS pixels = 200 device pixels.
  4. It means we need to render a 200x200px variant.
  5. If we’d render just the 100x100px variant instead, the image would stretch to 200x200 device pixels and would be of bad quality.

We use

  • Device pixel ratios: 1 , 2 , 3 ,4
  • Every image source rendered 4 times (1x, and then 2x, 3x and 4x bigger variants in size) for each size variant in sizes attribute

Art Direction

Art direction on various devices

Displaying images the same way on different screen sizes may lead to bad user experience. For instance, a wide image with a dog in the center (the main image focus) displays great on desktops, but on the same image on mobiles, the dog looks very small. As the dog is the main focus here, it’s important to display it in a suitable way.

Art direction is a technique of displaying the same images in different aspect ratios (or cuts) on different screen sizes.

We use

-<picture> element with combination of <source srcset="" sizes=""> and <img src=""> elements and attributes

Progressive Image Loading

Progressive image loading techniques: Blur-up and Background color

Slow loading of images has a negative impact on users. Until an image is fully downloaded, browsers usually display an empty space or are loading large images slowly from top to bottom. That might not be the best user experience.

Progressive image loading is a way of displaying smaller image versions, placeholders or colors while the original image is downloading. Once the download is finished, the original image can be displayed with a smooth transition. This gives a user the possibility to consume some information from the image before it’s downloaded in its full size.

We use

- Blur Up technique
- Background Color technique

Content Jumping

Loading of images causes content jumping by default

Loading images often cause annoying layout shifting which results in lost scroll position and user distraction. The browser does not know image dimensions when requesting it, so no space for the image is reserved and it has zero dimensions by default, invisible to the user. Surrounding content (text, visual elements, …) is loaded first, and when the image is fetched from the server, the content below the image jumps down about the image height in size. Not just it has a negative impact on the user experience, but it also makes the browser to do more calculations (repaints and reflows).

We avoid setting fixed dimensions to images due to the responsive layouts and since we know the image dimensions on the server, we use the aspect ratio padding trick.

If we divide the image height with its width and multiply the result by 100, we get the magic number that can be used to preserve space for the image. The trick is to add the inline style padding-top on the image wrapper, which results in two great things:

  1. No content jumping since space is preserved from the beginning of page rendering
  2. Adaptive image size by keeping the same aspect ratio on every resolution without the need to specify fixed dimensions

We use

-padding-top: ((imgHeight / imgWidth) * 100))% for the image wrapper
-position: absolute for the <img> inside

Responsive Images Code Generator

Since there are many combinations for image code, we decided to launch a simple application that enables generating the code based on passed options a user can set. Simply fill the needed parameters and grab the ready code.

🚀 To see it live, visit our imagenerator.

We are ACTUM Digital and this piece was written by Jiří Zmrhal, Front End Team Leader of Apollo Division. Feel free to get in touch.

--

--