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.
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?
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.
box-sizing: border-box; padding: 10px;
applied.Reply?
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 withheight: 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?
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?
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?
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?
That's approximately the concept of "viewport elements" already, except the width isn't allowed to change after the children are added either.
Reply?
"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?
Yup, I agree with Ian Taylor, and the notion of Element Queries I describe in my post match what he's asking for.
Reply?
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?
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?
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?
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?
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?
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?
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 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?
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?
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?
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?
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?
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?
I would love to get at least a JavaScript event to listen for element resizes. Then the developer can do all the sh*t it likes to do and it's not a problem of the specification anymore.
We have already MutationObservers, we could extend them adding a
size: true
config to allow sizes listening...Reply?
There is a proposal to add ResizeObserver, https://github.com/WICG/ResizeObserver.
ResizeObserver is currently behind a flag in Chrome, https://www.chromestatus.com/feature/5705346022637568.
There are existing attempts to polyfill the feature too (e.g. https://github.com/pelotoncycle/resize-observer), albeit the implementation relies on "getBoundingClientRect" which causes re-layout on every rAF. Therefore, unreasonable performance hit in production.
Reply?
What about EQCSS proposal/"polyfill":
@element {selector} and {condition} [ and {condition} ]* { {css} }
I still haven't seen any official W3C proposals for EQ. Any status or relevant public discussion about/aroud it?
Reply?
https://lists.w3.org/Archives/Public/public-respimg/2016Sep/0000.html doesn't say a lot, except that seems none is jet planning spec for it. Any status?
Reply?
Nope, status is still "lol I dunno".
On the "fundamental building blocks" side, CSS Houdini specs are progressing nicely - we should get the Typed OM and Parser API to CR early next year, which are the underlying techs for Custom At-Rules, letting you specify EQ rules in your stylesheet and have them handled by JS.
ResizeObserver is also trucking along very nicely, which is the fundamental JS piece we needed to do EQs efficiently.
The CSS 'contain' property also now has the "size" value, which is required to make the EQ container not depend on its children.
So, sometime in late 2017 we should have all the pieces needed to do full-fidelity EQs as a JS library. Based on experience with that, we can evaluate whether to move it into core CSS.
Reply?
Looking forward to using element queries in CSS!
Reply?
The problem with using iframes is that you can't embed them in a single source file, so to have a parent-responsive component you need to make a html per component. This makes server-side-rendering impossible :(
Reply?