#257: The One About Images

[Robin]: When it comes to web development I feel like images have been in a state of constant flux over the past few years. What’s the best way to add an image to a website? Well, that question is much more complex than it looks at first and, depending on the month, it seems like there’s new advice from every direction. This is partly a good thing because there’s been an awful lot of improvements made in this space.

For example, did you know about the image-set() CSS function? Me neither! According to this great post by Ollie Williams, it’s been supported in Chromium since 2012(!) and it just landed in Firefox 88. It allows us to load different images in CSS depending on screen resolution:

.element {
  background-image: image-set("platypus.png" 1x, "platypus-2x.png" 2x);

This might look familiar to you if you’ve ever used the HTML srcset attribute that looks something like this:

  alt="A baby smiling with a yellow headband."
  srcset="baby-highres.jpg 2x"

The browser will look at that 2x part and if it’s a higher pixel density display it will load that one instead of the low-res image. But let’s say you wanted to use a next-gen image format that isn’t supported in a lot of browsers right now, such as AVIF. You could then write something like this first:

.element {
  background-image: url('kitten.png');

This is the fallback image you want to use for browsers that don’t support image-set but then you can add to that code like this…

.element {
  background-image: url('kitten.png');
  background-image: image-set(
    "kitten.avif" type("image/avif"),
    "kitten.jpg" type("image/jpeg")

image-set lets you add a series of images that you want to load depending on the device. The first image here is the next-gen image format but if a browser doesn’t recognize it, then cool — it’ll ignore it and use the jpeg. Just like srcset allows you to do as well.

This is pretty dang amazing, especially as there are new image formats looming on the horizon like AVIF (which is a format already supported in Chrome and Android browsers).

Speaking of serving sharp images, Jake Archibald just wrote all about that. He writes about how to cater for low-res and high-res screens in a lazy way and also the most intense way imaginable by using the <picture> element. Here’s what Jake calls the lazy way:

Here’s the technique I use for most images on this blog: I take the maximum size the image can be displayed in CSS pixels, and I multiply that by two, and I encode it at a lower quality, as it’ll always be displayed at a 2x density or greater. Yep. That’s it.

That’s been my approach with most projects, too. But also dang is it great to know that the AVIF example Jake shows is about 37% the size of a regular ol’ JPEG.

How to cancel pending API requests to show correct data

I’ve noticed this problem on a lot of websites when I click around rapidly in a UI and the data is being fetched, only for the website to give me a really delayed response and visual update after the fact. Here’s an example:

See how the table changes the sort order and flashes even though you might think that it’s showing you the right data? In this post Georgi Nikoloff explains why that’s the case: we need to abort pending DOM asynchronous requests with AbortController. This chap can cancel HTTP requests and event listeners, which is pretty neat.

This post also reminds me that it’s a good time to go back and read about debouncing and throttling to help us handle events such as clicking or scrolling and to ensure the user experience is top notch.

Organize your CSS declarations alphabetically

In the past I’ve organized CSS in every which way; alphabetically, just adding stuff to a file incoherently, or by type of property, and in the end I don’t think I have strong opinions about this. But Eric Bailey sure does:

As someone who is really, really into CSS, I wish I could enthusiastically recommend an approach other than alphabetical. However, I’m unfortunately here to tell you not to.

The most common CSS declaration organization technique I come across is none whatsoever.

The more I think about Eric’s argument in this post, the more convincing it is, especially this bit:

I think this is an important thing a lot of people get wrong. You want to set up something sustainable, but also not pour your own energy into making the right thing in the wrong way just to satisfy your own personal desires.

Making sustainable practices is, oddly enough, sometimes more important than being the person in the room who knows the best way to do something. I forget that in an organization you might be the best person at CSS, but that doesn’t really matter. Instead of making this strict set of rules that no-one understands you need to make a system that works for folks with a broad range of skills.

This doesn’t mean that this solution is flawless. I can see there being problems with properties like top and bottom which are often needed to be changed in tandem with one another, and I can imagine it’s annoying going to the top of the “stack” and then hopping to the bottom. But then again, when you’re making a new standard then it’s often better for it to be predictable than it is to be…well…good.

When CSS Isn’t Enough: JavaScript Requirements For Accessible Components

Earlier this month I spotted a post from Stephanie Eckles about how CSS isn’t enough when it comes to making components accessible. The whole post is great but there’s a section in particular worth checking out called “determining if CSS-only is an appropriate solution” and, well, dang:

Does the feature include showing and hiding of content?
Then you need JS to at minimum toggle aria and to enable closing on Esc. For certain types of components that also change state, you may also need to communicate changes by triggering updates within an ARIA live region.

There are lots of accessibility situations that Stephanie digs into like tooltips, modals, carousels, dropdown menus, and tabs — each have their own complex needs when it comes to accessibility.

And JavaScript certainly plays a big role that we shouldn’t forget.

axe DevTools

Is your website accessible? Deque’s axe DevTools Pro makes accessibility testing easy for dev teams.

Find and fix potentially critical issues while you code. No expertise required. Build more accessible experiences and get started for free today.

Get the latest performance news and inspiration

Are you a tech professional that cares about the impact of your software on end-users? Then subscribe to the Performance Matters newsletter and get the latest software performance news and inspiration delivered to your inbox every week.

[Chris]: I was with some buddies in college one time, sitting at a diner. One of them thinks the waitress is cute and is stumbling over his words a bit. He orders a burger. She asks if he wants cheese on it. He says “Yeah. Preferably melted.” It was such a funny and ridiculous thing to say (especially on accident because of being tongue-tied). “Preferably melted” became a sort of college meme for us.

I was reminded of that when I tweeted two options for the <meta name="theme-color" ...> on CSS-Tricks. Go bright/bold? Or melt the background?

The people spoke:

Jaydn wasn’t alone — by far the most people liked the melted look. That one is easy, by the way, as you have to do nothing. Safari 15 will just pick up on the body background and do that.

In retrospect, the really narrow screenshot probably wasn’t a fair look. The orange looks so dominant on the left one, but if you see the whole browser window, it’s still bold, but maybe somehow less intense and I think looks like a stronger contender.

There were some other great ideas too!

  • What if it’s orange when you’re scrolled to the top of the page, but then as you scroll down it turns into a melted color? Love that.
  • What if it’s orange when your system is in light mode and melted to the dark background in dark mode? The syntax supports that now. It doesn’t mean your whole site needs multiple modes, it’s just responding to a global preference.
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black">