[Proposal] CSS @speed Queries


#1

CSS @speed Queries

A theoretical CSS query type for querying connection speed

One thing I feel that’s missing in CSS is knowing how fast a connection is. When we talk about connection speed, we’re usually referring to three things:

  1. Latency: The time it takes for requests to reach the server, and for the server to respond to them with content.
  2. Throughput: How fast your connection can receive and send data.
  3. Protocol: The current protocol being used. Could be h1, h2,. A value of h2 could be all encompassing for SPDY and QUIC (though SPDY is deprecated now.)

These all influence how fast websites load for us. What if we could query aspects of this in CSS? Let’s take a look at some quick use cases.

Querying latency

What if we could query connection latency? Here’s example of a theoretical min-latency media query:

/* Loading a retina image */
@media (min-resolution: 2dppx), (min-resolution: 192dpi), (-webkit-min-device-pixel-ratio: 2){
    .masthead{
        background: url("masthead-2x.jpg");
    }
}

/* Querying a slow RTT */
@speed (min-latency: 200ms){
    .masthead{
        background: url("masthead.jpg");
    }
}

Of course, you could always have a max-latency rule that would allow you do the inverse of this, but this feels a bit more intuitive to me, at least. The idea is that in high latency environments, the user agent could decide to sacrifice quality for high-DPI screens and download the smaller image.

Querying throughput

Latency isn’t the only environmental pressure on performance (though it is very important). Throughput matters, too. What if we had a max-downstream property we could query?

/* Loading a retina image */
@media (min-resolution: 2dppx), (min-resolution: 192dpi), (-webkit-min-device-pixel-ratio: 2){
    .masthead{
        background: url("masthead-2x.jpg");
    }
}

/* Querying a slow connection (in Kbps) */
@speed (max-downstream: 10000){
    .masthead{
        background: url("masthead.jpg");
    }
}

In the above example, masthead-2x.jpg would only download if the connection downstream was greater than 10 megabits. You could theoretically have a related upstream property, or min- variations of each, of course. You could use throughput and latency querying together:

/* Querying for either high latency or a slow connection */
@speed (min-latency: 200ms), (max-downstream: 10000){
    .masthead{
        background: url("masthead.jpg");
    }
}

Here we’re querying for either a high latency scenario or a connection with a low download speed. If either scenario occurs, the lower quality image would be transferred. You could use the and keyword to query for both conditions, but that would be far less flexible.

Querying the protocol version

This one is probably strays into the realm of esoterica, but it’s something I’ve done some writing on. HTTP/2 is great, but there’s a lot of browsers out there that may not support it, and some browsers like Opera Mini haven’t signaled their intent to implement support. When these users visit HTTP/2-capable web servers, the connection downgrades to HTTP/1. Since HTTP/2-specific optimization practices encourage less bundling/concatenation of files, this unduly increases the number of requests specifically for users that would be most affected by them. The size of this audience could be larger than you think.

So what if we had a protocol value we could query?

/* Serve individual icons by default (assuming the web server is HTTP/2) */
.icon.github{
    background: url("github-icon.svg");
}

.icon.email{
    background: url("email-icon.svg");
}

.icon.twitter{
    background: url("twitter-icon.svg");
}

/* Serve an image sprite if the protocol has downgraded to HTTP/1 */
@speed (protocol: h1){
    .icon.github,
    .icon.email,
    .icon.twitter{
        background: url("sprite.svg");
    }

    .icon.github{
        background-position: 0 0;
    }

    .icon.email{
        background-position: -2rem -2rem;
    }

    .icon.twitter{
        background-position: -4rem -4rem;
    }
}

protocol would probably be going too far, but could still be quite useful.

Why not just do this in JavaScript?

I have yet to speak authoritatively on the resource timing API, but I imagine one could tag the html element with a class in some of these instances, but the problem with this is that not everyone has access to JavaScript in all scenarios. Speed queries would admittedly reduce the scope of what could be controlled to only CSS, but the syntax would feel familiar and allow us enough control to change how we serve assets in different scenarios.

Questions? Feedback? Scorn?

Reply below. I have this mirrored at a Github repo as well.


#2

This is definitely an proposal that I’ve wished existed but always thought somehow was unfeasible for browser vendors to implement. It would really help with the responsive image loading use case as you mentioned. I find the latency and throughput queries to be most useful for this purpose.

I just wonder how connection information would be conveyed to the browser. This topic has been discussed before by Peter-Paul Koch in Measuring connection speed, ctd., and I find the closest feature we have today is probably client hints. Found some links to the topic, though they are dated a couple years ago. Even the W3C working group note on the The Network Information API was dated 2014.


#3

As I wrote a few years ago, Media Queries can’t be used for resolution negotiation. We invented <picture> to solve this problem properly, and that’s been implemented by all browsers for a while now. Just use that.

Deciding between sprite sheets and individual images is a different sort of beast that might be more reasonable. I don’t think you want to actually select based on transport type itself, tho; you instead want something that talks about the actual feature you’re caring about, which is how expensive multiple requests to a server are.


#4

Thank you for lending your expertise in response to this proposal. I understand your assertion that one should “just use” <picture> to solve the problem of responsive images for inline images (which is a fine answer), but it doesn’t address background images as CSS does, which necessitates a media query to serve the correct imagery according to a device’s pixel density.

Furthermore, this proposal aims to give the developer control of how these assets can be used (or not used) in specific performance scenarios. Perhaps a separate type of query isn’t the answer, and maybe it isn’t even reasonably possible-- but I do see that Chrome handles font rendering differently on slower connections with no intervention on the part of the developer. Perhaps it wouldn’t be such a stretch for the user agent to accept a lower quality background image and ignore background-image references encapsulated in high-DPI @media rules on slow connections?

The only problem with this is getting all browser makers on board with such a change, but I think this issue merits some consideration.

Afterthought edit: Isn’t it also possible to avoid caching issues by detecting what images already exist in the cache? For example, say the user visits the site on a high speed connection and downloads all of the high-resolution images on the first go. Those are placed in the cache. Then let’s say the same user visits the site again on a slow mobile connection. Couldn’t the bandwidth-detection logic be abandoned in this instance and the image could be pulled from the cache?


#5

The point @tabatkins was making in his article is that we cannot (and should not) define media queries for conditions that constantly change. And network conditions do constantly change. Hanging media queries off them will end up annoying users (constantly changing styles) and result in double downloading of resources.

I believe Tab mentioned <picture> as a reference to the general responsive images spec, within which srcset makes a better precedent here. srcset enables you to define multiple image resources (in varying densities or dimensions) and the browser can pick the one it sees fit in case of bandwidth stress. (It should be noted that no browser currently does, but the spec enables that)

In CSS, image-set enables us to do the same. Currently, only densities are defined but we can extend it to include dimensions as well.


#6

image-set sounds like it largely solves what I was looking for then, so long as the browser internals determine that a connection is slow and considers using the least burdensome of options. Thanks to both you and @tabatkins for chiming in and giving me some guidance. I really appreciate it.