Skip to content
This repository has been archived by the owner on Feb 16, 2023. It is now read-only.

CSS Containment Draft #7

Open
Wilto opened this issue Jun 17, 2014 · 27 comments
Open

CSS Containment Draft #7

Wilto opened this issue Jun 17, 2014 · 27 comments

Comments

@Wilto
Copy link
Contributor

Wilto commented Jun 17, 2014

So, there are two sides to this: the CSS side of things (“what do the element queries look like”) and the element side of things (“how do we define an element as having viewport-like qualities”).

We can’t treat every element as having viewport-ish behavior, or we end up with a fractal nightmare of reevaluation and potential infinite loops: an element sets a child element to width: 200% which cascades back upward and changes a series of parent elements each with potential element queries attached, and one of those causes the width: 200% EQ to no longer apply, and so on unto infinity. There have been a few things up for discussion: a specific “treat as a viewport” element or attribute among them.

http://dev.w3.org/csswg/css-containment/#containment seems like something we should be keeping an eye on, since it deals with special-casing elements independent of the rest of the page, though in a very different way.

@tabatkins
Copy link
Contributor

We can’t treat every element as having viewport-ish behavior, or we end up with a fractal nightmare of reevaluation and potential infinite loops: an element sets a child element to width: 200% which cascades back upward and changes a series of parent elements each with potential element queries attached, and one of those causes the width: 200% EQ to no longer apply, and so on unto infinity.

Nah, the point of viewport-ish behavior is that children have no effect on your layout, so it's okay. The reason we can't treat everything as having that behavior is because it's a terrible behavior for most elements, who really do want to be sized according to their children.

@Wilto
Copy link
Contributor Author

Wilto commented Jun 17, 2014

Ah, okay, thought it upped the “infinite loop” potential.

@uniqname
Copy link

I've been bouncing my thoughts on this off my co-workers but I apologize if it's still rather unformed or too low level right now. I just needed to get it out of my head and in front of those with a deeper understanding of it's implications.

Currently floated or inline-blocked elements take on a size imposed by their content/children. This is what sets up the infinite loop problem, no?

Is it possible to invert this model for an element that defines an element query? For example, when the CSS parser comes across an element query, anything matching the selector within it would be layout deferred until the rest of the those element's subtree's styles are applied. At that time the parent of the element using an EQ would have determined it's size as per the traditional model, based off it's children not using element queries. Then, children querying the parent element have accurate properties to query against with the layout engine enforcing that styles applied to elements using EQs do not affect the properties queried against on the parent element.

IOW. if an selector queries on it's parent being a certain width, and it applies a width to itself. The element would simply overflow it's parent container rather than affecting the parent container's width.

This would mean any styles applied to an element that is using EQs that may be defined in another stylesheet, and which affect the property being queried against, would still not affect the parent property. In effect the querying element would be but in an element query mode that would prevent the property styles being queried against from "bubbling" up.

I'm sure I'm missing some important issue here, apart from the probably dramatic impact to the layout engine this would have.

@tabatkins
Copy link
Contributor

Browsers aren't generally happy with drastically changing how they do layout based on style information, because that means they can't usefully start doing layout early (they might have to throw out a whole subtree of work when they realize it's viewport-ish). They want something that can be determined at the markup level, hopefully.

@uniqname
Copy link

I was hoping to avoid that with the deferring of rendering the specific element doing the querying, but I admit I don't know enough about layout engines to know if that would matter enough.

@tabatkins
Copy link
Contributor

"Rendering" is one thing, but layout calculations are another, and an element becoming viewport-ish can change the entire page's layout.

@davidjgoss
Copy link

This syntax idea is far from thought through, but I might be worth throwing into the ring.

@container ".block" and (min-width:615px) {

    .block__element {
        // rules
    }

    .block__other-element {
        // more rules
    }

}

This is expressing: "When there is an element that matches the selector ".block" whose computed style matches the query "min-width:615px", apply these rules only descendants of that element".

I'm hoping that having a selector for the container in the @container -part will avoid the need for an attribute in the HTML to declare an element that will be viewport-y --- that is, that the parser could do a sweep of the stylesheet to find out before starting to fully apply the styles.

Any thoughts?

@Wilto
Copy link
Contributor Author

Wilto commented Jun 17, 2014

Ah—suuuper early to start talking syntax. There are a lot of technical things we have to sort out way beforehand, just to see whether there can be a viable approach to this.

@davidjgoss
Copy link

@Wilto Yeah sorry, jumping ahead with the specifics of the syntax, but my main thought was whether the viewport- ish elements being declared in CSS with selectors like that would be an option technically as opposed to markup.

@yoavweiss
Copy link

+1 to having "viewportish" hints in markup. Is go further and say that the actual dimensions should be available at preload time, if we want to throw responsive images into the mix (and I think we would want that).
So they'd have to be either in the markup itself, or inlined

@tabatkins
Copy link
Contributor

I think that's unnecessarily limiting; having the element respond to its surroundings by, say, being in a grid layout is very useful.

We shouldn't restrict this more than the existing <iframe> case, I think.

@yoavweiss
Copy link

Possibly. We should be aware that not having the viewport dimensions at preload time would mean that internal responsive resources (mostly images) would have to relate to the global viewport, rather than to the element's viewport (or not be preloaded at all).

@pdaoust
Copy link

pdaoust commented Jul 3, 2015

I could be wrong, but I feel like, if CSS containment (specifically layout containment; paint containment isn't relevant for container queries) were accepted, this gets solved for us for free. A container query automatically gets an implicit contain: layout declaration.

@tabatkins mentioned that the hard work would be in convincing a browser to do relayout for a whole subtree once it discovers the container query rule. If browser vendors accept layout containment and the optimisation problems it presents, it wouldn't be hard to push container queries as an added feature. If they reject it, there'd be little hope for container queries, except as something that can only be applied to some sort of <viewport> element.

I woke up way too early this morning to my eight-month-old daughter saying "ba ba ba" and hitting me on the arm, which means "I know it's only 5:30 but it's time to play!" So my argument may be poorly thought out and I'd love to hear rebuttals.

Re: responsive resources (images et al), if the whole container queries thing goes through, the sizes attribute all of a sudden gets weird. It'd be most useful to subject images to the same container query goodness... but is sizes="(min-width: 36em) 50vw" referring to the global viewport or the container 'viewport'? Maybe the spec would state that it would be relative to the nearest containing viewport. Does this raise issues for multiple containing viewports? I'd guess not, because the closest container is probably always the one you should be thinking about from a CQ point-of-view.

@pdaoust
Copy link

pdaoust commented Jul 3, 2015

You know, another thing that gets weird is the vw unit and its cousins. Which viewport do they refer to if container queries are always creating new viewports? Again, I'd vote for 'nearest viewporty ancestor'.

@pdaoust
Copy link

pdaoust commented Jul 3, 2015

Just thought of something else. If contain: layout prevents contents from influencing the size of the container (as if it were overflow: auto) what happens to flow? I don't want my container to be influenced by the width of its children, but I do want it to be influenced by the height of its children. Now what? contain: layout-x? That seems ugly.

@tabatkins
Copy link
Contributor

If browser vendors accept layout containment and the optimisation problems it presents, it wouldn't be hard to push container queries as an added feature.

It's actually substantially harder. Layout containment is just giving the ability to delay some parts of layout, and lay out other parts of the page in parallel safely. Responding to nested layout via selectors is much crazier, requiring swapping back and forth between layout and styling engines multiple times during display of the page.

Which viewport do they refer to if container queries are always creating new viewports?

CQ containers aren't actually viewports, just viewport-ish. The viewport units still refer to the real viewports. If we want something to respond to the CQ container, we'd want a new unit.

I don't want my container to be influenced by the width of its children, but I do want it to be influenced by the height of its children.

This is why proper CQ needs something a bit less restrictive than contain:layout, that can be switched for each axis.

@pdaoust
Copy link

pdaoust commented Jul 6, 2015

@tabatkins thanks for the response. I think I need to understand containment a little better. Am I correct in understanding that it creates the opportunity for parallel layout precisely because it declares "this element's children will have no influence on its size, and thus have no influence on other bits that need layout"? And following from that, once you eliminate side-effects, you open the door to parallel processing. So there could never be such a thing as contain: layout-x because then there'd be side-effects. Right?

But because container queries would need the flexibility to constrain in one dimension but not necessarily the other, they really can't take advantage of contain: layout. Is that kind of what you're explaining?

I think I understand all that. The part I didn't quite understand was

Responding to nested layout via selectors is much crazier, requiring swapping back and forth between layout and styling engines multiple times during display of the page.

Could you elaborate a bit more on that?

If we want something to respond to the CQ container, we'd want a new unit.

Oh! That's a fairly elegant solution.

(By the way, re: responsive images and container queries, @aFarkas pointed out to me in another conversation that image selection could never happen at parse time if we wanted images to be chosen based on a container element, so we wouldn't need to do anything special to get responsive images to support container queries — except the ability to defer image selection to layout-time rather than parse-time, or a similar solution.)

@tabatkins
Copy link
Contributor

So there could never be such a thing as contain: layout-x because then there'd be side-effects. Right?

Right. contain is all about enabling parallel layout and cheap, predictable-cost updates and such, by very strictly limiting how much of an effect something can have on the rest of the tree.

But because container queries would need the flexibility to constrain in one dimension but not necessarily the other, they really can't take advantage of contain: layout. Is that kind of what you're explaining?

Yes.

Could you elaborate a bit more on that?

Sure. contain:layout doesn't do anything crazy, fundamentally. It flips some switches in the layout algorithm, and that's it. (There might be some complicated stuff going on to take advantage of optimization opportunities, but the plain effect is simple and easy.)

CQ is a whole different beast. In order to lay out a page, you have to do style resolution (eval selectors, cascade, etc), then start layout. You have to stop at CQ boundaries, then when the rest of the page is finished (thus giving you a definite size for the CQ containers), do style resolution again so you can correctly resolve styles on the CQ descendants. Then more layout inside of the containers, again stopping at any nested CQ boundaries, then more style resolution, etc.

It's a horrid flipping back-and-forth between systems that are built with the assumption of being one-and-done, at least in a given layout pass. All styles are resolved, then all layout is done, that's that. Rearchitecting to handle the back-and-forth is actually a big project.

This is why doing CQ right, inside the browser, is so hard. It'll be much easier to expose the tools necessary to do CQ 95% right (resize listeners on every element, switches for "ignore children when computing width/height", and custom at-rules/selectors (so you can write your CQs directly in your stylesheet, where they belong, rather than doing them in a script)), and that's what I'm pursuing right now.

@pdaoust
Copy link

pdaoust commented Jul 6, 2015

Awesome, thanks so much for the insight. You obviously know a thing or two about the inner workings of browsers (oh ha, just looked at your bio and I see that may be a gross understatement); thanks for filling in the gaps for me that I wasn't aware even existed.

the tools necessary to do CQ 95% right (resize listeners on every element, switches for "ignore children when computing width/height", and custom at-rules/selectors (so you can write your CQs directly in your stylesheet, where they belong, rather than doing them in a script))

So what's the other 5%? That looks like a pretty complete set of features to me! :-)

@tabatkins
Copy link
Contributor

The other 5% is making them work correctly when you change the width of a CQ container in script and then immediately do getComputedStyle() on a descendant. A correct and full implementation would respect CQs there and give you correct information; an implementation in script with the tools I outlined won't. (You have to get back to the event loop and wait for a resize event to hit, so you can adjust styles, before the contents of a CQ container will give correct results.)

@pdaoust
Copy link

pdaoust commented Jul 6, 2015

hmm, so without that 5%, any script that relies on the computed styles of the children to be correct would have to do something like requestAnimationFrame() before it accesses those computed styles?

@tabatkins
Copy link
Contributor

Yeah, something like that.

@Wilto
Copy link
Contributor Author

Wilto commented Mar 2, 2017

This viewport-ish behavior is, ironically enough, something that gets kinda talked in circles. How do we distill this down to a one or two line capital-R Requirement, a la http://usecases.responsiveimages.org/#requirements ? @tabatkins, it feels like:

A correct and full implementation would respect CQs there and give you correct information

is about halfway there, yeah? Any thoughts there?

@Wilto
Copy link
Contributor Author

Wilto commented Mar 2, 2017

( Starting to get those requirements together in #36 )

@eeeps
Copy link
Collaborator

eeeps commented Feb 13, 2018

So, I read through this today, and — @tabatkins is several steps ahead of everybody, as per usual. A lot of this discussion deals with browser internals/constraints, which is really useful to know and understand, and will heavily influence implementation/spec discussions, later.

As for our current work, on use cases and requirements, the two clear takeaways for me are:

  1. We need some kind of requirement that browsers need to be able to do this work performantly. That's frustratingly vague, but I worry about making the requirement any more specific. Should maybe spin up a separate issue for this?
  2. I took it for granted that any solution, native or otherwise, would give accurate sizing information in JS, immediately. Silly me! I suppose this relates to the “frame behind” worries in Can we require no FoUC/jank on initial load? #37. I wonder if there's a succinct way to write a requirement that ties both together.

Spitballin’:

Container-conditional style rules must not take effect any later in the browser’s layout flow than other types of conditional style rules; for instance: rules within a media query. Once a container size has changed, browsers should not paint any old or intermediate layout states, or deliver old or intermediate layout information about a container or its children in response to a getComputedStyle() call.

Personally, I would be extremely open to changing/dropping this requirement, in response to feedback from browser vendors and spec people. I don't know enough about the web platform to know what rules are ok to bend or break.

@beep
Copy link
Collaborator

beep commented Feb 13, 2018

Should maybe spin up a separate issue for this?

+1! And I think frustratingly vague is a fine starting point, especially for v1.

@eeeps Would you like to fire up the new issue, or shall I?

@eeeps
Copy link
Collaborator

eeeps commented Feb 13, 2018

@beep I'll give it a shot, hopefully this afternoon.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants