Web Development > Beyond CSS Media Queries - A Conditional Pseudo Class

Note, since writing this article in Jan 2013 the concept discussed below seems to have aquired itself a name: "Element Queries". It seems lots of other people were having the same ideas independently and at the same time. I've included some relevant links at the end of the article, but for now here is my take on the situation and how to resolve it.

Responsive design is still big news, and for good reason, it makes a lot of sense. These days people are consuming web pages and using web apps on a variety of media and we need to offer the best user experience we can on whatever device is being used. The methods of achieving this have been widely discussed elsewhere, so I will not repeat that here, apart from to mention one of the main tools in our armory, media queries.

Now, don't get me wrong, media queries are great. Targetting your styles at media types (screen, print, etc.) and media features (width, height, etc.) is very powerful, but I can't help feeling like we're doing it a little wrong. Let me explain...

Let's take an example of a page with a menu. When the page is wide enough we want the menu to be horizontal, however if the page gets too narrow we shift to a vertical menu. This is easily achieved with media queries (you can guess the HTML)

.menu .menu-item {
    display: inline;
}
 
@media all and (max-width: 500px) {
    .menu .menu-item {
        display: block;
    }
}

But, hold on a minute, there's already something wrong here. I've coupled my menu styles and my page dimensions. I've complected them - this is bad.

To see why, suppose I change my page layout a little so that now I have an 80px wide company logo next to the menu, say. This results in less room for the horizontal menu despite the same page width. So now I have to dig around in my CSS to find the media query and bump up the 500px limit to 580px, euch!

So what is the solution? How do we decouple our menu styles from the page dimensions?

Well, ideally we would like a pure CSS solution as this is a problem of layout, not semantic structure or behaviour. Wouldn't it be nice if we could do something like this:

.menu .menu-item {
    display: inline;
}
 
.menu:where(width < 500px) .menu-item {
    display: block;
}

Whoa! What's that :where()thing I've just made up and stuck in there?! It's a CSS pseudo class that would allow you to refine your selector based on some specified condition. When trying to match elements to the section of selector text which contains the :where() pseudo class, the CSS engine would have to consider whether the elements also match the specified condition, as well as the usual selector text matching. It's completely analogous to other pseudo classes, e.g :hover which will only match the element if it's also being hovered over.

The condition in this case is on the width of the .menu element being less than 500px, and I'm thinking of that width as being the used value of the width, i.e. the same value as obtained through a call to getComputedStyle.

Wouldn't it be great? Styling elements conditionally based on the computed styles (used values) of other elements. In our example, the menu would no longer be coupled to the page, it would only know about itself. I'd be free to dump that company logo next to the menu without having to go fiddling around in my CSS and changing all the media query break points. I could share this menu component across different sites. Hell, I could even let someone host my page in an iframe and not have it screw up my nice responsive design.

Of course, there is no :where() pseudo class, I'm dreaming. As with all dreams you have to wake up at some point.

The obvious pinch in the arm is this: Specifying conditions in terms of the computed style would add further layout cycles to the rendering process. Naively, we could think of it working something like this

  1. Evaluate all conditions in :where() pseudo classes as false, since we have no used values yet.
  2. Perform a layout of the document with all conditions as just evaluated.
  3. Evaluate all conditions using the current used values of the relevent elements.
    1. If all conditions evaluate to the same as previously, stop.
    2. Otherwise, go to 2.

Of course, beyond the obvious performance hit, there is no guarantee that this process would terminate. The following very simple example gets stuck in an infinite loop

#test {
    width: 50px;
}
 
#test:where(width < 100px) {
    width: 200px;
}

Well, I'm going to live in hope that these problems can be sorted out. Somehow.

In the mean time, we can shim this behaviour using a bit of JavaScript. I've created some code and simple examples of it in use over on GitHub: cssconditionals. It's currently an initial proof of concept, as such there are a couple of bits missing

  • Stylesheets should be processed to replace the :where() pseudo classes with an identifying ordinary class. Currently you just write this ordinary class directly e.g. .menu.where-width-lt-500px .menu-item { ... }
  • Currently changes to layout are not listened to, as such there is no recursive loop as mentioned above. For now we're just doing the calculation every 400ms on a setInterval

If you'd like to see a quick demo of this in action it's available here, but make sure you check out the GitHub repository if you want to see the latest code.


As mentioned earlier this concept has since taken on the name of "Element Queries", many people seem to have come to the same conclusions, independently, at around the same time. Below are some links to some relevant articles.