Skip to content

Meta

My website: Responsive images

Image generation is handled by a custom Astro integration, similar to @astrojs/image, and all images are wrapped in a custom Figure component for richer content and better semantics. The image optimisations are carefully synchronised with the layout. Aspect ratio awareness is mandatory to eliminate CLS.


Content and semantics

The Figure component:

  • Adds <figure> and <figcaption> semantics.
  • Provides content for the image caption.
  • Provides an attribution for the caption (example below).
  • Renders the Image component

The resulting HTML

<figure>
  <span>
    <image alt="{alt}" src="{...}" />
  </span>
  <figcaption class="cs-primary astro-DA36CKTJ">
    {image title}
    <span>
      {image attributiom}
      <a data-external href="{attrbution url}">Visit site</a>
    </span>
  </figcaption>
</figure>

Eliminating CLS

Images always take more time to load than the rest of the content. This causes CLS which impacts usability of the page negatively, as things shift around while it loads, and may deteriorate SEO ranking.

When rendering images, we acquire the width and height of the image via getImageFacts() and set the absolute (natural) dimensions directly on the HTML <img width="..." height="..." />

This allows the browser to reserve the space for the image, even if we are styling it with CSS, using something like width: 100%; height: auto. Aspect ratio will be respected as long

Don't you just 😍 evolution? It only took 20 years. Before browser support for aspect-ratio we had to resort to padding-top + percentage technique. Big kudos to @Princesseuh

Example

Code:

![Photo of wall with stickers](./images/example.jpg 'Have a nice day, beautiful decay! // Photo by Ann H https://www.pexels.com/@ann-h-45017/')

Result

Photo of wall with stickers
Have a nice day, beautiful decay! Photo by Ann H Visit site

ℹ️ If you block the URL in the browser developer tools and refresh the page, the space required for the image is still preserved.

Image profiles

Note: Back in 2019, native responsive images were still a bit of challenge. But today, issues like @srcset and picture being a maintenance nightmare are a non-issue if you are using a framework like Astro and generating all picture, source and img tags from within a component.

I need to specify which resolutions I want to generate and this depends on how the image is going to be displayed by the website's layout. This might be a bit tricky if you are using responsive layouts, and even trickier if you are using a fluid responsive scale for typography and a responsive space scale. But it is still doable.

The goal here is to serve higher resolution for hero images, and medium resolutions for images inlined in posts, and to have the browser select which resolution to load depending on the viewport's width. Also, for things like thumbnails, I probably want a single resolution, and even smaller.

All profiles are declared here: images/profiles.ts.

Default profile

The content column layout is never wider than ~700px so I are generating images only at two resolutions:

  • width: 700px - loaded on all small screens
  • width: 1400px - loaded if the screen is wider than 1200 pixels

This should speed up download on all mobile devices.

ℹ️ If you resize the screen above/below 1200 and then click open image in new tab, you will get the different resolutions.

Hero profile

For hero images, which take up 100% of the width of the site's <main>, I extend the previous profile and add one extra resolution:

  • width: 700px - loaded on all small screens
  • width: 1400px - loaded if the screen is wider than 650 pixels
  • width: 2800px - loaded if the screen is wider than 1200 pixels

Image formats

Out of the box @astrojs/image was generating jpg, avif, and webp versions (x2 resolutions). A simplistic look at can I use avif vs can I use webp indicates that I've got only Microsoft Edge to gain or loose.

-rw-r--r--  461K  dist/assets/file....1400-size.jpg
-rw-r--r--  126K  dist/assets/file....700-ize.jpg
-rw-r--r--  433K  dist/assets/file....1400-ize.webp
-rw-r--r--  123K  dist/assets/file....700-ize.webp
-rw-r--r--  294K  dist/assets/file....1400-ize.avif
-rw-r--r--  93K  dist/assets/file....700-ize.avif

Since avif yields more compression (~30% smaller files) I'll drop the webp format and use only avif and jpeg.

All browsers get small files, except Edge. Faster downloads better experience. Also, my builds are sped up because smaller / less files to output and upload. 🌲 Less energy consumption as well.


Go back to top of the page