About a year ago, I talked about Element Queries and why they were difficult to make work. It's about time for an update.
Short version: Not much has changed. Naive element queries still suffer from circularity issues, and there's been no real effort to specify anything to get around it. However, Shadow DOM seems well-placed as a way to get an EQ feature into the web.
What Are Element Queries Again?
An "element query" is like a media query, specifically the 'width'/'height' MQs, but they apply to the width/height of the element's container, rather than of the screen or device.
Why Were Element Queries Problematic Again?
Element Queries (let's just call them EQs from here on) suffer from basic circular dependency issues.
Here's a simple example: Say you have an element which is normally 100px wide, but it uses an EQ to say that if its container is less than 200px wide, the element becomes 500px wide. (This is silly, but bear with me.) If the container is explicitly sized (
width: 150px; or the like), this is fine, but if the container is sized to its contents (
float: left;, for example), then you have a circular dependency: if the element is 100px wide, the container is 100px wide, but that trigger the EQ, so the element is 500px wide, so the container is 500px wide, but that disables the EQ, so the element is 100px wide, so the container is 100px wide, etc.
What About Them
A funny detail of this is that EQs already exist, at least in some form. If you use a MQ on a page displayed in an
<iframe>, the MQ is resolved against the size of the
<iframe> element. This is exactly an EQ, if you treat the
<iframe> as the "container" in the above explanations.
This avoids the problems outlined above because an
<iframe> cannot depend on the size of its contents. An
<iframe> is only ever explicitly sized; if it doesn't have anything explicit, it defaults to being 300px wide and 150px tall. The contents never affect its size in any way, and so the circularity problem never comes into effect - if you get super wide because the
<iframe> is skinny, nobody cares; the
<iframe> just sprouts a scrollbar.
How Can We Get Around This?
Of course, you don't want to use
<iframe>s all over your page. It would be nice if we could somehow establish the same sort of situation while keeping all our content in the page together.
The first thought people usually have is to create some CSS value, perhaps a
display value or something like that, which makes the element no longer pay attention to its contents. Unfortunately, this doesn't work well - since you don't know which elements are "independent" until you apply CSS, you might do too much work on initial layouts before you figure out that you're all wrong. Also, since the boundaries aren't immediately clear (and in fact can shift over time, if the property starts/stops applying to various elements), it's somewhat difficult to understand what elements are allowed to use EQs.
Better would be something that would alert browsers to the "independent" nature of the element before they start applying CSS. For example, an HTML attribute would work for this. Unfortunately, it's annoying to have to sprinkle presentation-based attributes over your page just to get your styling to work right.
I think that we've got a good opportunity to make this less bad with Web Components, though. They already represent a useful "boundary" in the form of the shadow DOM, so the potential confusion of not knowing which element your EQ is applying to would go away - it always applies to your host element, assuming it's marked as "independent".
While Level 1 of the Shadow DOM specs are stabilizing right now, I think this is something reasonable to aim for as a Level 2 feature. You'd be able to set some switch when defining your custom element, or perhaps set it per
ShadowRoot, which would make your component's layout independent of its contents, just like
<iframe>. Then, EQs would work inside the shadow, referring to the size of the host element. Outside of a shadow, or in a non-"independent" shadow, EQs would just always be false.
So that's the state of Element Queries in 2014. We're not really any closer to having them than we were in 2013, but there's light on the horizon for a possible good solution.
Height:% already solves the circular dependency problem by refusing to work if container has height:auto.
How about having element MQs match only elements with non-auto width?
Maybe match against value of the width property rather than computed width?
to answer my own question: it won't work, because code in EMQ can change value of the property as well.
There is an EQ solution that works just fine: https://github.com/Snugug/eq.js
Seems like your hypothetical example is a silly one and not a good reason to say that EQ's "can't" work. They work just fine if you don't try to set fixed width sizes that are larger than your container (which you are defining the element queries for).
That's not "an EQ solution", it's a JS library that's willing to make certain allowances. JS libraries can do lots of things that are hard or impossible to do in the real language, because they don't have to be full-fidelity. They can do things like do a single layout run and settle on whatever result comes out, because they don't have compat constraints with other browsers.
The example is a silly one because a real example is more involved to set up, but would end up with the same effect. We can't define something that works fine unless you do something wrong. At minimum, we still have to define what happens when you "do something wrong"; ideally, we have to define something that works correctly all the time, or at least minimizes the chance of "doing something wrong". It's pretty amazing how often "something wrong" ends up being a valuable use-case that we just didn't anticipate at first.
The Shadow Dom doesn't seem unreasonable place to limit EQs. But I'm not convinced of the argument about it not being clear which elements are allowed to use EQs if that behaviour is only enabled by a setting another property first.
This happens all over CSS – top and left do nothing by default. Vertical-align only apples to inline elements, etc. The context is a little different with EQs, but is that such a problem?
I am as usual writing a reply before really mulling the details though, so I may change my tune after I've thought it through a bit more.
They're not actually comparable. Yes, some properties only have an effect if some other properties are set, but this is about reinterpreting potentially large sections of a stylesheet, based on some element somewhere higher up in the tree. We very purposely try to limit these types of interactions in CSS, so that most properties depend only on other properties on the same element, and a few depend on properties on the parent.
QML resolves that very simply: if it detects a circular dependancy it just bails out and throws a warning.
A convention should be agreed on which of the two should be ignored (possibly the one that comes second in the stylesheet, or even both) and go on with parsing the rest of the styling
When you set an EQ to an element, you vould use a display value inside the query to make it 'active', so to speak. It would resemble the use of 'content:" "' on pseudo-elements. Then the size of the content of the element would be ignored when it matches the EQ.
Just a first reaction, I'm probably overlooking some stuff here...
How about just going for it, and leaving the circular dependency issues undefined? They would be extremely useful in many cases, and it's really not like CSS is perfect or that great anyway, web developers are relatively used to hacks to get things working between all browsers etc.
In fact when it comes to web apps and complex web sites, CSS is being completely misused. There is a vast difference between a document (the original intention of the WWW) and an "app".
CSS is used because it's the only option. It's like if there was only one food in the world, it doesn't matter how rubbish it tastes or what it's made from, you're still going to eat it because you have no choice.
The use of the web is evolving fast and as usual the standards are lagging heavily behind.
"Just leave it undefined" isn't a real answer. Everything still gets defined, it's just done piecemeal and independently, by individual developers as they code up the solutions and come up with their own ways to resolve the problems.
Then people write pages and only test them in one browser, so when it looks good they ship it, even if it was accidentally depending on the "undefined" behavior as that one browser defined it. When this happens with a big, popular site, other browsers get bug reports because they page looks "wrong", and they have to reverse-engineer what the first engineer did and implement it on their own, which is expensive, buggy, and slow.
Or we can avoid all this pain by just defining it up-front, but that's also hard, and the interest is low. It's easier to get support behind something that doesn't require this effort, by avoiding the circular dependency entirely.
I came up with a proposal for element queries which tries to solve the recursion issue: https://au.si/css-container-queries The idea behind it is to let the browser select the “right” container for the element query.
I also created a prolyfill which implements this proposal: https://github.com/ausi/cq-prolyfill