Tab Completion

I'm Tab Atkins Jr, and I wear many hats. I work for Google on the Chrome browser as a Web Standards Hacker. I'm also a member of the CSS Working Group, and am either a member or contributor to several other working groups in the W3C. You can contact me here.
Listing of All Posts

Element Queries

Last updated:

I'm getting more and more requests for "Element Queries", and it's getting annoying to address them via Twitter, so I thought I'd discuss the limitations and challenges of this idea right here and just point people to it when required.

What's an Element Query?

The idea of Element Queries is pretty simple - it's like a Media Query (specifically, the min-width/etc queries), but for a parent or ancestor element, rather than the viewport.

This way, you could create a "component" that is styled one way when it's in a narrow container (like a sidebar), but another way when it's got some breathing room (like in the main content of the page), without having to manually specify what kinds of containers are each type - it just naturally happens due to the width of the respective containers.

Circularity

The first and most obvious problem with EQs is circularity.

There are lots of ways in CSS for an element's width to depend on the size of its contents. If the contents can then respond dramatically to the size of the element, you've potentially got problems. For example, say you had something like this:

.container {
  float: left;
}
.child {
  width: 500px;
}
.container:min-width(450px) > .child {
  width: 400px;
}

In this simple example, the container's size is determined by the size of its contents, because it's floating. By default, its only child is 500px wide. However, whenever the container is greater than 450px wide, the child is 400px wide.

In other words, if the child is 500px wide (thus making the container also 500px wide), then the child becomes 400px wide. But if the child is 400px wide (thus making the container also 400px wide), the child becomes 500px wide. Circular dependency!

This already happens today in some circumstances, such as an overflow:auto container with contents that have an aspect-ratio. If the aspect ratio is "tall", then reducing the width will shrink the height by a larger amount. If the content just barely overflows, scrollbars will pop in, reducing the width of the container, which shrinks the height of the contents enough that they no longer overflow. Thus, you don't need a scrollbar, and it disappears, making the content wider and taller, so it overflows, so you need a scrollbar, so the content becomes narrower and shorter, and you don't need a scrollbar...

We could potentially do a similar thing with EQs, but it's much less clear how it would work, since there are so many more variables than with scrollbars.

Avoiding Circularity

It would be much better if we could just avoid circularity entirely, and the web platform already shows us how to do so: iframes! Iframes are elements in the page, but they establish a brand new viewport for their contents, so you can use normal MQs inside of them to style the contents differently based on how big the iframe is, just like an EQ. They avoid circularity issues by locking down their own width/height - the size of an iframe doesn't depend on its contents at all. If you don't explicitly specify their size, iframes default to a size of 300×150.

Taking this over to EQs, we'd need some way to declare that an element was a "viewport element", and didn't pay attention to its children at all when sizing, instead defaulting to some specified size (probably 300×150 to be consistent) when necessary. Then, its contents could be allowed to refer to its size in selectors, and style themselves accordingly.

Unfortunately, this still has issues...

Viewport Elements

Even if you avoid circularity, "viewport elements" have further issues in implementation.

The concept of EQs already requires a bit of weirdness, in that you can't run selectors once, first, and then do all your layout - you have to go back and forth, doing layout and then selectors and then layout again. However, because of iframes, this is already something you need to do, and further, you know up-front that you don't need to lay out an iframe's contents or run its selectors/MQs until the outer document is finished laying out, so you're never throwing away any work and starting over, just putting some off until later.

The most natural way to declare an element to be a "viewport element", though - a new CSS property or value - breaks this. You'd need to do run selectors on the whole page, run the cascade, figure out which elements are viewport elements from that, and then rerun selectors on the children again. This is wasted work, and it's slow and annoying to deal with in the implementation.

Boris Zbarsky, Moz hacker, says he'd be okay with viewport elements and EQs if they could be detected from the markup. This means adding some styling information to your document markup, which isn't ideal. But maybe it's okay? I dunno.

So, that's the state of the art right now.

(a limited set of Markdown is supported)

#1 - Jon Rimmer:

Rather than adding a viewport element, could this be added to the web components API? So a custom element can declare itself to be a viewport element when it registers. Thinking out loud, might that also allow for interesting things like letting components opt-out of regular layout for their contents and do it all themselves via script?

Reply?

(a limited set of Markdown is supported)

Whenever I read about these kind of "styling based on the size of the parent element" I worry the whole thing is getting too complicated. Can't we just define it as using "the width of the parent's content-box* before the child is added" and then not calculate in circles? It would be functionally the same in at least 9/10 cases.

  • I really just want to know how much room the element has, without having to worry that the query is lying because the parent has box-sizing: border-box; padding: 10px; applied.
Reply?

(a limited set of Markdown is supported)

Why can't we just make EQ work only when the element's width or height (for corresponding EQ) is fixed (== is not affected by children)?

So, a float or inline-block with fixed width wouldn't ever change its width based on the children. For overflow: auto we could always use the outer box etc.

For blocks with display: block all we need to know is if it's fixed. It would be there almost like it's now with height: 100% actually.

Yeah, the point is that they won't work in any circumstances, but, hell, it would work in a lot of scenarios!

What I miss?

Reply?

(a limited set of Markdown is supported)

Thanks for the clear explanation Tab :)

FWIW, I'd be fine with adding an attribute in mark-up to buy this functionality. I don't like it conceptually, but the pay-off is worth it IMO.

Failing that I'm likely to investigate the whole Web Components thing to see if it can be hacked to do something similar. I'm getting mighty bored with authoring components that respond to the viewport differently on different pages, it's like chinese whispers or something.

Reply?

(a limited set of Markdown is supported)

Its interesting how this could be used, but it still doesnt feel like there is a full proof solution for all devices yet. I read Ian Taylors interesting view on this: http://ianstormtaylor.com/media-queries-are-a-hack/

Reply?

(a limited set of Markdown is supported)

Re #1: That might be a reasonable way. I'm looking into other ways that avoid Boris's problems without having to push the info all the way up to HTML.

Reply?

(a limited set of Markdown is supported)

Re #2: That's approximately the concept of "viewport elements" already, except the width isn't allowed to change after the children are added either.

Reply?

(a limited set of Markdown is supported)

Re #3: "Only when fixed" fails a bunch of responsive use-cases, so that's a non-starter.

More importantly, it's exactly as hard as making it work only when viewport: yes or whatever is specified, but weaker. You still have to do all of the cascade and style resolution, then return back to selectors and run everything over again.

Reply?

(a limited set of Markdown is supported)

Re #5: Yup, I agree with Ian Taylor, and the notion of Element Queries I describe in my post match what he's asking for.

Reply?

(a limited set of Markdown is supported)

Re #4: If you take some trade-offs for granted (you'll have to avoid circularity, for example) I am very sure it can be hacked, especially if some extra markup would be used as a workaround.

Reply?

(a limited set of Markdown is supported)

From an authoring point of view I think viewport elements sounds limiting but from an implementation point of view it looks like we've solved many of the problems with it. I think better authoring experience wins though.

I had a think about syntax and came up with this: http://maketea.co.uk/2013/04/11/proposal-context-aware-css-selectors.html

Reply?

(a limited set of Markdown is supported)

#12 - Ned Baldessin:

What about using a constraints system? It was studied nearly 15 years ago (see http://www.cs.washington.edu/research/constraints/web/ccss-uwtr.pdf ), but has never been adopted. I've never understood why. It seems like an ideal solution for this problem of building interlocking/responsive UI modules. OSX 10.8 has evolved to use a constraint system for UI, IIRC.

Reply?

(a limited set of Markdown is supported)

Re #12: Believe me, there are very smart people thinking about constraint systems (my coworker, Alex Russell, does occasional work in this realm).

It's not something you can just "switch on", though - you need to first rebuild all of CSS layout in terms of constraints. That is an extremely non-trivial task.

Maybe we'll get there someday, and I applaud the efforts of those trying to achieve it. I won't be directing my efforts that way, though.

Reply?

(a limited set of Markdown is supported)

#14 - Ned Baldessin:

Re #13: Oh yes I'm also sure smart people have studied the question and there's a good reason why we don't use constraint systems today… I was just wondering what that reason might be! :)

You raised the architectural reason (we can't rebuild everything or make it backwards compatible), but I could easily imagine a performance reason, or maybe a more theoretical reason, like the circularity you mention in your (great) article.

Cheers.

Reply?

(a limited set of Markdown is supported)

I found http://www.xanthir.com/b4PR0 a helpful place to start in understanding why your perfectly reasonable desire (which I, as I'm sure many, have hit some version of) is . . . in tension with the basic rendering model of CSS. Viewports (== lightweight iframes) are a long way from the naive understanding of what EQs would give, because they require fixed widths, independent of context.

Can you see a way forward which doesn't require rethinking/implementing the entire rendering model/engine?

Reply?

(a limited set of Markdown is supported)

Re #15: They don't actually require fixed widths independent of context, just that the element you're querying doesn't depend on the elements affected by the query in any way. To make this predictable, the stuff in the "can query" set and the stuff in the "can use query information" set should be causally disconnected as far as layout is concerned, and viewport elements (/iframes) do that.

Reply?

(a limited set of Markdown is supported)

Re #16: A very gracious reply -- I'm embarrassed that I was trying to point Ian Storm Taylor at your stuff, and got my windows confused. But your comment is helpful in any case.

Reply?

(a limited set of Markdown is supported)

I'm sure I'm missing something but could the 'Clown car technique' ( http://coding.smashingmagazine.com/2013/06/02/clown-car-technique-solving-for-adaptive-images-in-responsive-web-design/ ) be of any use here? It seems that media queries can be run from within an element that has an indeterminate width. Directly ported this might require a new element to contain these EQs, but could some of the implementation techniques there be applied more broadly?

Reply?

(a limited set of Markdown is supported)

As a matter of fact, we have a second viewport element besides iframe in HTML 5: svg. It is also nestable, i.e., you can have multiple svg elements stacked into one another to establish viewports at will.

It would be great, if we could just re-use the html or body element for this: place it somewhere in your markup, and tada: new viewport. Unfortunately hell would break lose on 99% of current web pages if this would be allowed. (Think of $('body').)

Reply?

(a limited set of Markdown is supported)

Re #18: As I note in the post, you can use an <iframe> to get Element Queries working today. SVG similarly establishes a new viewport, where the size is independent of the content, and thus gets to benefit the same way.

"Directly porting" this is the hard part.

Reply?

(a limited set of Markdown is supported)

Here's a half-assed attempt to get past the problem of having to go back-and-forth to consider the cascade/layout. Introduce a new @viewport at-rule (which, like @import, must precede other rules):

@viewport {
    selector: "#someId",
    name: "viewport1"
}
@viewport {
    selector: ".someClass",
    name: "viewport2"
}
@media screen and (viewport: viewport2) and (viewport-min-width: 300px) {
    /* rules apply to children of .someClass when it is >= 300px */
}
@media all and (viewport-max-width: 400px) {
    /* rules apply to children of any viewport when it is <= 400px */
}

This doesn't address the circularity issue, but there were some other proposals about that already…

Reply?

(a limited set of Markdown is supported)

Re #4: Yeah, because in all honesty we are already simulating this functionality with an HTML attribute 90% of the time: an extra classname to specify the change. So to trade the classname for a hook that would allow the MQ functionality is a win in my book. :-)

Reply?

(a limited set of Markdown is supported)