Thursday, April 18, 2013

Element Queries

Element Queries
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.