Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Proposal for a slightly different syntax and function #3
Comments
ausi
commented
Aug 5, 2015
|
To better describe the idea for the browser implementation here is an example: Lets go with a simple example DOM: <html>
<body>
<div class="container">
<div class="child"></div>
</div>
</body>
</html>And the following CSS: body {
padding: 10px;
}
.container {
float: left;
}
.child:container(min-width: 150px) {
background: green;
}And lets assume a viewport of 800x600. In the compute style step the browser would do the following:
|
BenjaminPoulain
commented
Aug 5, 2015
|
Ok, that makes sense. You do need to lay out twice. You would need to split the layout algorithms in two: a first pass only doing the simplest size computation top-down for independent properties only, followed by a second pass doing dependent properties after you do your second style recalc. I am afraid the model is a bit complicated. It won't be easy to understand which parts of a document is viewport dependent, and which parts are independent. I quite like it to be honest. It would be hard to implement but it is a step in the right direction. |
ausi
commented
Aug 6, 2015
|
Thank you for the great feedback! So you would suggest first doing a style calculation without container queries, then a descendants-independent layout algorithm, then a style calculation including container queries and then the second pass of the layout algorithm as the last step? Wouldn’t it be better for the performance to do the first pass of layout directly in the style calculation step? |
BenjaminPoulain
commented
Aug 6, 2015
|
Layout is incredibly complicated. Having a internally consistent style during the entire layout is a useful simplifying assumption. I am not even sure you could do layout with no style for all descendents but I can't think of an example against that at the moment. Style collection and style resolution are pretty self contained. They are not particularly efficient but layout is an order of magnitude worse. I would not be too concerned about an extra style collection && resolution step if-and-only-if the total layout time remains unchanged (or is improved). |
ausi
commented
Aug 6, 2015
|
The problem I see with splitting the style calculation and the first pass layout is that they have to run multiple times if elements with container queries are nested. In the worst case one time per nesting level. Layout is incredibly complicated, but AFAIK the parts that would be needed in the „first pass“ for the container queries should be much simpler. E.g. the direction for the computation is strict top down, it’s never necessary to know something about the child nodes to calculate the „container-width“. As you can see in step five of the example above, in cases where the full layout algorithm depends on descendants, the „first pass“ layout just computes |
davatron5000
commented
Aug 6, 2015
|
Pardon my ignorance, were we to explicitly define container elements would that help avoid or speed up the second evaluation pass? Could a property trigger those children to be added to a collection that could then be evaluated? body {
padding: 10px;
}
.container {
float: left;
layout: container;
}
.child:container(min-width: 150px) {
background: green;
} |
ausi
commented
Aug 6, 2015
|
In my example above the body element is used as the container for the query. As I read it again now, I should’ve named it The problem with explicitly defined containers is that the size of them may depend on its descendants. That the „right“ container is chosen by the browser is important IMO to solve the recursion issue and preventing CSS authors from mistakes. The CSS code you posted would result in a recursion if you add some dimensions: body {
padding: 10px;
}
.container {
float: left;
layout: container;
}
.child {
width: 200px;
}
.child:container(min-width: 150px) {
background: green;
width: 100px;
}For a complete demo of the recursion problem with the current syntax, you can take a look at ResponsiveImagesCG/cq-demos#2. |
davatron5000
commented
Aug 6, 2015
|
Okay I think I'm understanding it all more. This makes a lot of sense.
Q: What about inheritance? Would the CQ evaluate on the inherit div.parent {
}
.child:container(min-width: 150px) {
width: 100px;
} |
ausi
commented
Aug 6, 2015
|
Yes it would. In my example above A You can take a look at the functions isFixedSize and isIntrinsicSize of my prolyfill to see how it works there. It isn’t complete but handles the usual cases already. |
ausi
commented
Oct 4, 2015
|
If someone is interested: I posted an update on my proposal and added a demo page for the prolyfill. The prolyfill script is now tested and pretty stable, so it should be ready to play around with it: https://github.com/ausi/cq-prolyfill. |
ausi
referenced this issue
in ResponsiveImagesCG/cq-usecases
Oct 6, 2015
Open
What's acceptable for the element query polyfill #32
maxhoffmann
commented
Oct 13, 2015
|
Have you thought about calling it
|
ausi
commented
Oct 13, 2015
|
IMO What about A word which would make the selector readable as a sentence would be great too IMO, like it is with |
maxhoffmann
commented
Oct 16, 2015
|
In my opinion In CSS
Isn't an element's query condition always retrieved from its parent node? The parent might have inherited the queries value but it still has the information, doesn't it? |
ausi
commented
Oct 17, 2015
This is an implementation detail IMO and could be different in various browsers. As I’m no native speaker I don’t really know what fits better, I would be OK with any of But I think its a bit early for discussions about the correct name for container queries. We should check first if the proposed functionality is usable for CSS authors and implementable for browsers. As @BenjaminPoulain already mentioned “I quite like it to be honest. It would be hard to implement but it is a step in the right direction.” I’m optimistic for the browser side and my current intention is to get CSS authors to play around with the prolyfill. |
stefanklokgieters
commented
Jan 6, 2016
|
Is there any news regarding making a formal proposal towards W3C? Is there any news regarding browsers developers integrating this technology in their products? can see a lot of benefits using this functionality. Thanks! |
ausi
commented
Jan 6, 2016
|
@stefanklokgieters IMO before making a proposal towards W3C, we need to know from browser makers if this version of container queries is any better than the others and if it is possible to implement it in a performant way. |
davatron5000
commented
Jan 6, 2016
|
Agree: Let's not get jammed up on syntax right now. Let's stay focused on the ideal functionality. After many months of mulling this over, I think this is a pretty fool proof way to avoid the infinite recursion problem. I think we'd need feedback on:
I'd be happy to start soft-balling this to browser people for feedback on this technique. In the meantime maybe we should get some prolyfill performance stats on 1, 10, 100, 1000 container -queries being applied? Even if it's just JavaScript we can start getting an idea of time/memory footprint. |
davatron5000
commented
Jan 6, 2016
|
Feedback from someone on the Chrome team:
Overall the need for isolated module styling was understood. This 2-pass layout thing seems like an issue but will keep asking around for feedback. I'm also putting together a little explainer doc so it's easier for people to catch up on the multi-year conversation. |
ausi
commented
Jan 7, 2016
|
@davatron5000 Thanks for your help!
I will look into that and report my findings here. |
d6u
commented
Jan 7, 2016
|
A React implementation perf example using rAF:
Looks like the 1000 one suffers a lot when resizing the window. But scroll performance is OK. But all of the demos are simple, might not be what you wanted. Implementation details see https://github.com/d6u/react-container-query/blob/master/src/createContainerQueryMixin.js#L27-L54 |
ausi
commented
Jan 10, 2016
|
I tested the prolyfill with a simple container query: div:container(width > 500px) {
background: green;
}The result:
I also tested different nesting levels but that doesn’t change the result that much. |
d6u
commented
Jan 10, 2016
|
@ausi A dumb question, how did you measure the resize time and initial time? Any doc/code I can learn from? |
ausi
commented
Jan 10, 2016
|
@d6u I wrote a quick script to measure the speed, you can take a look at it here: https://gist.github.com/ausi/0f30d7568d2f93c04fa3 |
jonathantneal
commented
Jan 30, 2016
|
Great work, @ausi. This looks fantastic. |
dbaron
commented
Feb 18, 2016
|
I think limiting queries to elements that have ' If you don't have that limitation, then you have the problem that the size of the element (which you're querying on) can be influenced by the contents of the element, which can in turn be influenced by whether the query matches. #3 (comment) suggests this is doable with two passes, but I'm not quite sure I see how that works for handling of dynamic changes. The fundamental problem with handling dynamic changes is that we want a small change to content (e.g., adding a character of text) to have a small cost to re-layout. If you have an algorithm that's fundamentally two-pass, then you either (1) need to re-layout everything back to the first pass state and then re-layout everything again back to the second pass state, or (2) maintain separate data structures of the first pass and second pass states. For nesting of elements that use container queries, (1) would yield an exponential cost in time and (2) would yield an exponential cost in memory usage; I don't think either is likely to be acceptable as a performance/memory characteristic of the Web platform. (That said, I'm not sure that flexbox and/or grid haven't made this mistake -- which may be related to performance problems people are having with flexbox.) (How does your JS implementation handle these cases?) Even with the limitation to elements with ' |
ausi
commented
Feb 18, 2016
|
@dbaron thank you for taking a look at it! If I'm understanding
My idea is to let the browser select which element the query gets matched against (the nearest qualified ancestor), so that a recursion cannot happen. But this selection only requires one dimension to be not influenced by the contents, the other (non-queried) dimension may still depend on the contents. Maybe adding a dimension to the
For small changes in the document, step 2 wouldn't find any changed container queries and no additional layout is needed. For most changes that trigger a container query, one second layout is needed. In the worst case the number of additional layouts is as high as the nesting level of container query elements. |
dbaron
commented
Feb 19, 2016
|
So saying the browser can select which element the query gets matched against isn't really a useful answer. Which element will it actually select, and how will it handle that? I can see that you'd want auto-sizing in one dimension, though. However, I don't think doing layout containment in only a single dimension is sufficient. It's not clear to me there's a sensible way to benefit from that across all of CSS's layout algorithms (e.g., flex, grid), some of which are rather complex. Though maybe they've managed to preserve some clever invariants, but I doubt it. So one problem with the algorithm that you describe in #3 (comment) is that it's dependent on what the previous layout was. We generally try to avoid making layout algorithms work such that you can get a different layout for the same DOM depending on the sequence of mutations that led to that DOM (though this may not be quite true for non-overlay scrollbars). But perhaps that's an ok invariant to break. It is scary, though, since it will lead to bugs where a site has a different display depending on the ordering of its incremental loading process (since incremental loading is effectively a sequence of dynamic changes) -- so effectively race conditions in layout. This also assumes that you've set things up so that the size (in the one dimension) of the qualified ancestor can't be influenced by the selectors. This is nontrivial; it probably requires most aspects of style containment, and it's far from clear to me that it's generally true across table layout, flex layout, and grid layout if you introduce something like single-axis layout containment. For some container query algorithms, it might be ok if this sometimes failed in edge cases. But combined with an algorithm like the one you described that's dependent on the previous state, it's pretty bad, since you could get into cases where each successive re-layout produces a one of two alternating states (or, with multiple queries, possibly even more complicated state machines), which is probably an even worse race condition. (The comparison here is against an algorithm that is expected to compute everything from an initial state right up front, but this has the problems I described in #3 (comment) .) |
ausi
commented
Feb 19, 2016
For a width-query it will select the nearest ancestor whichs width doesn't depend on its contents. My JS impelementation currently traverses the DOM tree up until it finds an element with a fixed width, from this element it then traverses the DOM tree back down as long as the elements widths depend on their parent. It does that by checking the style of the elements against some simple rules, the rules for grid and flexbox are not (yet) implemented. One issue with this algorithm are scrollbars, which could change the inner width of an element depending on contents.
That was the most performant way I found for the JS implementation because I cannot hook into the layout process of the browser. An implementation in the browser may work differently. |
tigt
commented
Mar 16, 2016
|
As far as baby steps go, would something like |
ausi
commented
Mar 16, 2016
|
@tigt AFAIK it would be much easier and it is already implemented in iOS I think. Auto-resize iframes are currently being discussed in the www-style mailing list: https://lists.w3.org/Archives/Public/www-style/2016Mar/0198.html. |
henriquea
commented
Mar 29, 2016
|
@ausi good stuff! I like the idea about |
ausi
commented
Mar 29, 2016
|
@henriquea Thanks! |
ausi commentedAug 2, 2015
In #2 (comment) I posted an idea about a different syntax. It looks like:
And the nearest qualified ancestor is selected as the container to run the query against by the browser.
As I put more thoughts into this I got an idea of how the implementation issue (jumping between compute style and layout) could possibly be solved.
If I’m right the browser computes the styles by traversing the DOM tree from top to bottom. In this process it could already calculate and store the width if it knows that it doesn’t depend on its descendants. If it then reaches an element with a container query rule it already knows what the right container is – it’s the nearest ancestor for which it was able to calculate a width – and which width it has. So it should be possible to resolve the container queries without doing the layout process.
It may be that I’m totally wrong with my assumptions about browser internals, but it would be great if it is implementable this way.
If someone is interested in this idea I also wrote a prolyfill and a blog post about this version of container queries.