Skip to content

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

Open

Description

@dholbert

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?)

Activity

  1. cbiesinger commented on Apr 17, 2021

    @cbiesinger

    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 )

  2. Loirooriol commented on Apr 17, 2021

    @Loirooriol
    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...

  3. chrishtr commented on Apr 27, 2021

    @chrishtr
    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.

  4. chrishtr commented on Apr 27, 2021

    @chrishtr
    Contributor

    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.)

  5. Loirooriol commented on Apr 27, 2021

    @Loirooriol
    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.

  6. chrishtr commented on Apr 30, 2021

    @chrishtr
    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?

  7. Loirooriol commented on May 1, 2021

    @Loirooriol
    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";
  8. chrishtr commented on May 11, 2021

    @chrishtr
    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?

  9. tabatkins commented on Jun 7, 2021

    @tabatkins
    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!

  10. chrishtr commented on Jun 7, 2021

    @chrishtr
    Contributor
  11. cbiesinger commented on Nov 10, 2021

    @cbiesinger

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

  12. dholbert commented on Nov 11, 2021

    @dholbert
    MemberAuthor

    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*

  13. Loirooriol commented on Aug 12, 2022

    @Loirooriol
    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.

  14. bfgeek commented on Aug 17, 2022

    @bfgeek

    (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.
  15. Loirooriol commented on Aug 17, 2022

    @Loirooriol
    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.

  16. emilio commented on Aug 17, 2022

    @emilio
    Collaborator

    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).

  17. css-meeting-bot commented on Aug 17, 2022

    @css-meeting-bot
    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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions