Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-sizing-4] Clarify spec about when elements are allowed to not have a last remembered size #6220

Open
dholbert opened this issue Apr 17, 2021 · 17 comments

Comments

@dholbert
Copy link
Member

Currently css-sizing-4 section 5.2.1 "Last Remembered Size" says:

An element might not have a last remembered size, if it has never been rendered without size containment. (In this case, it will instead use the fallback value provided along with auto.)

This statement logically implies that its contrapositive must hold, i.e. it seems to be saying: "If an element has ever been rendered without size containment, then it must have a last remembered size".

I'm not sure that we want to require this, though. Consider e.g. these scenarios:

(1) an element is rendered, and then becomes display:none (losing its css boxes). And then it gains contain:size, and then loses display:none (e.g. going back to display:block). Do we want to require (or imply) that its last-rendered-size should survive this process?

(2) an element is rendered (gaining a Last Remembered Size), and then gains contain:size, and then has its display value changed in a slightly-less-destructive way (e.g. from display:inline to display:grid). Do we want to require/imply that its last-rendered-size should survive this process?

(3) an element is rendered, and then gains contain:size, and then is reparented to somewhere completely different in the DOM tree. Do we want to require (or imply) that this element's last remembered size must survive this process?

In all three of these cases, the element has previously been rendered without size containment, so the spec's caveat about "an element might not have a last rendered size..." seems not to apply, i.e. it seems like the spec assumes that the element would have a last rendered size. But I'm not sure if that's intentional or not.

(Put another way: is the intent here that the last-rendered-size is something attached to the node, or something attached to the CSS box?)

@cbiesinger
Copy link

FWIW, in terms of Blink implementation, we have decided that we have to use an actual resize observer and store the last remembered size in the DOM node. We have not thought of a way to get the right semantics without actually using resize observer (container queries make it hard).

(cc @bfgeek @vmpstr @tabatkins )

@Loirooriol
Copy link
Contributor

Also, what about a dynamic contain-intrinsic-size? Like

  1. You render an element with contain-intrinsic-size: auto 0px and no containment, so it remembers the size, e.g. 100px.
  2. You set contain-intrinsic-size: none.
  3. You change the contents so that the inner size becomes e.g. 200px. This is not recorded as the last remembered size.
  4. You add contain: size; contain-intrinsic-size: auto 0px.

Do the 100px survive this process?

we have to use an actual resize observer and store the last remembered size in the DOM node

How do you handle ::before and ::after? Can you add a resize observer to a pseudo-element? AFAIK Blink doesn't create pseudo-element nodes if they are not going to generate a box, and disposes of the nodes if they stop generating a box...

@chrishtr
Copy link
Contributor

Do the 100px survive this process? (@Loirooriol)

No. Once you remove the auto keyword it loses memory of the past. Do you see an issue with this behavior?

(Put another way: is the intent here that the last-rendered-size is something attached to the node, or something attached to the CSS box?) @dholbert

I agree with what @cbiesinger said: it should be a property of the element, not the box. Otherwise its behavior will I think be too unpredictable for developers.

@chrishtr
Copy link
Contributor

chrishtr commented Apr 27, 2021

How do you handle ::before and ::after? Can you add a resize observer to a pseudo-element? AFAIK Blink doesn't create pseudo-element nodes if they are not going to generate a box, and disposes of the nodes if they stop generating a box...

No I don't think you can attach a resize observer to a pseudo-element. Therefore I think we should amend the spec to exclude pseudo-elements from the auto behavior. (Also because I don't know of a good use case for it anyway.)

@Loirooriol
Copy link
Contributor

I don't see a problem with clearing the remembered size when removing the auto keyword, that's what I would expect. But the spec should say so.

@chrishtr
Copy link
Contributor

Ok. Proposed spec edits:

  • auto behavior only applies to elements which can be ResizeObserver targets (in particular, excluding pseudo elements for now)
  • auto value is forgotten when another a ResizeObsever opportunity occurs again and auto is not present at that time (meaning the update-the-rendering steps occurred).

@tabatkins WDYT?

@Loirooriol
Copy link
Contributor

No strong opinion, but maybe I would even expect to remove the last remembered size during the first layout after removing auto, even if there hasn't been any ResizeObserver opportunity.

// Assume last remembered size is 100px
element.style.containIntrinsicSize = "none";
element.offsetLeft; // Forces sync layout, clears remembered size?
element.style.containIntrinsicSize = "auto 0px"; // Re-add "auto" before ResizeObserver opportunity
element.style.contain = "size";

@chrishtr
Copy link
Contributor

No strong opinion, but maybe I would even expect to remove the last remembered size during the first layout after removing auto, even if there hasn't been any ResizeObserver opportunity.

The problem with this definition is that it's not clear how to define it. e.g. what if some other element's offsetLeft was queried - does that require layout on element or not?

tabatkins added a commit that referenced this issue Jun 7, 2021
@tabatkins
Copy link
Member

Yeah, given that we're already leaning heavily on RO timing for this, if possible I'd like to stick with that; it keeps things simple and predictable.

I've gone ahead and editted in the conclusions summarized by @chrishtr in 22a5e0e. Lmk if that satisfies everyone!

@chrishtr
Copy link
Contributor

chrishtr commented Jun 7, 2021

@tabatkins LGTM!

@cbiesinger
Copy link

@dholbert could you confirm that the latest version satisfies your concerns>?

@dholbert
Copy link
Member Author

dholbert commented Nov 11, 2021

Yeah, this seems like a reasonable way to address this.

Minor nitpick: right now, this issue's added text (in https://drafts.csswg.org/css-sizing-4/#last-remembered ) is worded as follows:

At the time that ResizeObserver events are determined and delivered [...] record the current inner dimensions of its principal box as its last remembered size.

There's a little ambiguity here, because if there are no ResizeObservers, then arguably there's not any time when ResizeObserver events are determined and delivered. (There are no events to be determined or delivered.) So that sort of implies that last-remembered-sizes just never get recorded in a document without ResizeObservers.

Maybe this should instead be phrased more hypothetically, like so:

At the time that ResizeObserver events would be determined and delivered (if there were ResizeObservers registered): [...]

But maybe not a big deal. *shrug*

@Loirooriol
Copy link
Contributor

In #7527 we resolved

If, when an element goes from not generating a principal box to generating one, it does not have cis:auto, forget any last remembered size

Given that, I'm now more convinced that the last remembered size should be forgotten during the first layout after removing auto, even if there hasn't been any ResizeObserver opportunity, as I said in #6220 (comment).

Otherwise it doesn't look consistent:

// element has last remembered size
element.style.containIntrinsicSize = "none";
element.offsetLeft;
// element still has last remembered size
// element has last remembered size
element.style.display = "none";
element.offsetLeft;
element.style.display = "";
element.style.containIntrinsicSize = "none";
element.offsetLeft;
// element doesn't have a last remembered size

Also, it's simpler from an implementation point of view to just clear the last remembered size when laying out.

I don't buy this argument:

The problem with this definition is that it's not clear how to define it. e.g. what if some other element's offsetLeft was queried - does that require layout on element or not?

Since unless I'm missing something, whether offsetLeft causes layout is already webexposed e.g. with transitions
element.style.transition = "width 1s";
element.style.width = "100px";
element.style.width = "200px";
element.clientWidth; // 200 (if we started with width:auto)
element.style.transition = "width 1s";
element.style.width = "100px";
element.offsetLeft;
element.style.width = "200px";
element.clientWidth; // 100

Agenda+ to discuss removing the last remembered size without waiting for ResizeObserver when laying out without c-i-s:auto.

@bfgeek
Copy link

bfgeek commented Aug 17, 2022

(I might miss the meeting this morning - likely be around for the end - but want to make sure that we generally keep ResizeObserver timing for this feature). The ResizeObserver timing is important for a few reasons.

  1. The lifetime of boxes aren't defined. E.g. all engines have subtle rules around when the box-tree is re-built, when nodes are re-used, etc. For example in Blink fieldsets can cause re-attachment where in other implementations they don't (one among many examples).
  2. What APIs trigger layout (and how much layout) isn't defined. E.g. for offsetLeft/etc. you can currently have a conforming implementation which performs a partial layout just to answer the offsetLeft query for example. We shouldn't tie more side-effects to forced-sync-layouts lightly.

@Loirooriol
Copy link
Contributor

Well, even if the lifetime of boxes and the APIs that trigger layout aren't defined, in #7527 we resolved

If, when an element goes from not generating a principal box to generating one, it does not have cis:auto, forget any last remembered size

so we already depend on that.

@emilio
Copy link
Collaborator

emilio commented Aug 17, 2022

Right, I think we already depend on when styles are "visible" to the engine via transitions and such. @bfgeek is right that this is not really terribly-well defined (that's #5115).

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-sizing-4] Clarify spec about when elements are allowed to not have a last remembered size, and agreed to the following:

  • RESOLVED: When laying out an element, if contain-intrinsic-size is not auto, remove last-remembered-size immediately
The full IRC log of that discussion <emeyer> Topic: [css-sizing-4] Clarify spec about when elements are allowed to not have a last remembered size
<emeyer> github: https://github.com//issues/6220
<emeyer> oriol: Followup on FTF discussion
<emeyer> …If an element is not generating a box, if the intrinsic size isn’t auto, the last remembered size will be forgotten
<emeyer> …You can take a look at my comment in the issue
<Rossen_> q?
<jensimmons> present-
<emeyer> …When we are laying out an elem,ent, it’s a good time to observe or not observe an element, but I have to observe an element to see if I should forget last remembered size and then unobserve it
<emeyer> …Makes more sense to say with c-i-s stops being auto, we should immediately remove last remembered size (l-r-s)
<emeyer> …Some were arguing against that, argument is that it’s not well defined what should happen
<emeyer> …I don’t see a big problem and would prefer to switch to a more consistent behavior
<emilio> +1 to oriol, the undefinedness of flushing layout/style info is https://github.com//issues/5115 and very long-standing
<emeyer> TabAtkins: no opinions, I need to talk about this more with Ian to see what’s what
<emeyer> emilio: We have a long-standing bug about this, I think the c-i-s is probably not the place to draw the line
<emeyer> …I think I agree with oriol, we should do this whenever we style the document and can re-evaluate whether elements need l-r-s
<emeyer> Rossen_: What’s the one-line resolution?
<emeyer> oriol: When laying out an element, if c-i-s is not auto, remove l-r-s immediately
<emeyer> TabAtkins: no objections now, but want to preserve right to object later
<emeyer> Vladimir: Does this interact badly with container queries?
<emeyer> oriol: Not sure, to be honest. Will have to think about that.
<emeyer> emilio: The remembered size doesn’t affect stlying, but does affect layout. I think you need size containment to cause this
<emeyer> …I don’t think it’s a problem but worth thinking about a bit more
<emeyer> Rossen_: Doesn’t sound like a strong pushback, and we can bring this back if we have issues down the line
<emeyer> RESOLVED: When laying out an element, if contain-intrinsic-size is not auto, remove last-remembered-size immediately

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

No branches or pull requests

8 participants