csswg-drafts icon indicating copy to clipboard operation
csswg-drafts copied to clipboard

[css-position] Static position of abspos top layer elements inside fixed pos.

Open emilio opened this issue 2 years ago • 17 comments

This is the cyclic / ordering issue that I was concerned about in #8040.

Both Firefox and Blink get it wrong. WebKit gets it right, but I'm not sure what they're doing, maybe they're laying out the abspos twice?

<!DOCTYPE html>
<meta charset="utf-8">
<style>
#outer {
  position: fixed;
  width: 200px;
  height: 200px;
  border: 2px solid blue;
}
dialog {
  display: inline;
  position: absolute;
  inset: auto;
}
</style>
Some content.
<br>
<br>
<br>
<br>
<br>
Some more.
<br>
<div id=outer>
  Fixed-pos.
  <dialog>Absolute popover inside fixed element</dialog>
</div>
<script>
  document.querySelector("dialog").showModal();
</script>

The main issue here is that you have:

  • Viewport box
    • ICB
      • Abspos
    • Fixedpos

So you lay out the abspos before the fixedpos, and the static position isn't computed yet.

cc @rlbuis @bfgeek @chrishtr

emilio avatar Feb 13 '24 01:02 emilio

My preferred fix for this, fwiw, is that when an element is top-layer'd, it loses its static position. (We default it to 0,0 or something equally trivial.) We have Anchor Position now, there's no need to continue twisting ourselves into knots to maintain this clunky static-pos behavior in weird cases.

This would kill the cyclic problem in all permutations of this issue.

tabatkins avatar Feb 13 '24 16:02 tabatkins

@rwlbuis do you know what WebKit is doing here?

emilio avatar Feb 13 '24 17:02 emilio

@rwlbuis do you know what WebKit is doing here?

Tim is more an expert here.

rwlbuis avatar Feb 14 '24 15:02 rwlbuis

@Emilio Not sure this answers your question, but here's where we implemented the containing block adjustments:

https://github.com/WebKit/WebKit/commit/4f453d4c712f2989a3998bc9f604df3d95c8be20 https://github.com/WebKit/WebKit/commit/9dffcf60597eab8cefc3792f7886abdc38b39075

nt1m avatar Feb 14 '24 15:02 nt1m

@nt1m so webkit parents all top layer elements (abspos and fixed pos) to the viewport rather than the root scroller? Isn't that incorrect?

emilio avatar Feb 14 '24 22:02 emilio

I agree that Chrome appears to render this example wrong (I made the example into a jsbin here). What's wrong about it is that it has an incorrect static position that includes the content under the ICB ("some content", a few <br>s, and "some more"). Since the containing block of the abspos element is the viewport in this case, those should not contribute to its static position.

It looks like one can see the bug more clearly in this demo which uses position:relative and top:10px, which positions correctly in Chrome. Before adding top:10px the static position is also wrong, but after adding top:10px it renders correctly in Chrome.

Perhaps there is an issue with circularity, or at least extra complications, in computing static positions correctly in cases like this, though I think it's just a straight-up bug in Chrome that I think could be fixed by computing static positions separately in the top layer. @bfgeek WDYT?

chrishtr avatar May 10 '24 21:05 chrishtr

I think could be fixed by computing static positions separately in the top layer.

Not sure about that. You can have this case (abspos element in the top layer depending on fixed pos not in the top layer), but you can also trivially have the opposite. Also, you can have an abspos in the top layer depend on a fixed pos in the top layer and vice versa too, right?

emilio avatar May 10 '24 22:05 emilio

You can have this case (abspos element in the top layer depending on fixed pos not in the top layer),

It doesn't depend on it though, right? Which is achieved by reparenting its box in the layout tree.

but you can also trivially have the opposite. Also, you can have an abspos in the top layer depend on a fixed pos in the top layer and vice versa too, right?

I don't think there should be any layout dependency. The top layer should be completely independent of the ICB and its descendants.

chrishtr avatar May 10 '24 22:05 chrishtr

Well that's what this issue is about. The dependency comes from the static position, because that is usually "the position the box would have if not abspos" (and thus if not in the top layer).

emilio avatar May 11 '24 19:05 emilio

Update: looks like it actually renders correctly in Chrome. It pushes down the static position because the top layer is a sibling of the HTML element in the layout tree. Here's an example with an artifically larger HTML element (see red rectangle).

Note: in a few times while making that demo I observed wrong static positioning for the top layer element, which seems like a race condition in Chromium. @bfgeek says he might know what caused that, but it's an implementation caching bug, not a spec issue.

chrishtr avatar May 22 '24 20:05 chrishtr

I'm confused, so in chrome the top-layer reparenting also reparents the regular in-flow position? That is... not what I'd expect...

I mean, that makes the static position rather useless I guess, but that means that we have some leeway here and @tabatkins's proposal (making top layer elements just lose its static position) shouldn't be a webcompat issue (which is something that I feared initially).

emilio avatar May 22 '24 21:05 emilio

I'm confused, so in chrome the top-layer reparenting also reparents the regular in-flow position? That is... not what I'd expect...

What do you mean by "reparents the regular in-flow position"? Not sure what you're saying..

chrishtr avatar May 22 '24 22:05 chrishtr

Basically that the static position doesn't reflect the position the element would have if not top layered (which is what WebKit and Gecko do). It moves the static position to after the html element.

emilio avatar May 23 '24 06:05 emilio

It moves the static position to after the html element.

Ok thanks for explaining.

That's correct, but it's what I would expect. Why would you want the static position to reflect the position it would have if not top layered? It shouldn't retain such positioning right? Because the top layer is defined to reparent and relayout in full...

chrishtr avatar May 23 '24 21:05 chrishtr

The CSS Working Group just discussed [css-position] Static position of abspos top layer elements inside fixed pos., and agreed to the following:

  • RESOLVED: Make the static position of top layer boxes not depend on the size or position of any other element on the page, likely by making static position rectangle the ICB
The full IRC log of that discussion <TabAtkins> relative static position of absolute positions, fixed
<fantasai> emilio: This is the case where the static position of the abspos that depends on something laid out after it
<fantasai> emilio: annoying because cyclic
<fantasai> emilio: it seems what Blink does is making the static position at the end of the HTML element or something?
<fantasai> emilio: so mostly ignored
<fantasai> emilio: Gecko [missed]
<fantasai> emilio: And WebKit does something else that's incorrect
<fantasai> emilio: Tab's proposal is to ignore static position in top layer
<fantasai> iank_: I believe what happens is we reparent the top layer boxes in the box tree
<fantasai> iank_: so it's a box tree construction effect, not a layout effect
<fantasai> emilio: Conceptually they're reparented anyway because abspos
<fantasai> iank_: Blink doesn't have the concept of a static positoin placeholder like Gecko does
<fantasai> iank_: so as if you moved the placeholder and the thing during box tree insertion
<fantasai> emilio: Think it's best to ignore the static position
<fantasai> iank_: I think we've defined it to be, this happens at box tree time
<fantasai> iank_: or is it defined as something not that?
<fantasai> emilio: in Gecko we have two boxes for this element
<fantasai> emilio: but conceptually the abspos box (primary box) does get reparented
<fantasai> emilio: the static position, conceptually to me, is the position it would have if it wasn't abspos and thus not in top layer
<fantasai> iank_: but if this gets reparented during box tree...
<fantasai> iank_: when you insert abspos into tree in Blink, we don't reparent to the containing block. It sits whereever it lives
<futhark> q+
<fantasai> emilio: but it gets laid out and painted in a different order
<fantasai> emilio: behavior Blink has is super weird, so prefer to ignore it
<fantasai> iank_: you could just reparent the static position box to the same place?
<fantasai> emilio: yeah but I think it's super weird
<fantasai> iank_: but that's what the spec says?
<fantasai> emilio: I think that depends on whether the spec meant to include the static pos
<fantasai> iank_: staticpos is spec fiction
<miriam> ack futhark
<fantasai> futhark: Spec currently says "rather than generating boxes as usual, generate boxes as sibling of root element"
<fantasai> futhark: so spec says what we do
<fantasai> futhark: if that's not what we want, then we need to change the spec
<fantasai> emilio: I'm not opposed to close no change and test+clarification in the spec but... it does feel a little unusual
<fantasai> emilio: idk if that would cause issues, because then when destroying boxes you cannot reach ... if you remove an element in the DOM, can't reach descendant elements that are top layer
<fantasai> emilio: prefer to behave as regular abspos, parented to the relevant containing block
<fantasai> emilio: I'm fine with either behavior
<emeyer> fantasai: Could someone describe the situation that crfeates a cycle?
<emeyer> s/crfeates/creates/
<emeyer> fantasai: The top layer escapes the fixedpos to go into the top layer?
<emeyer> emilio: Right.
<miriam> ack fantasai
<emeyer> fantasai: Why is the top layer element not asbpos?
<emeyer> emilio: Top-layer things escape their usual containing block rules
<emeyer> iank_: Right, they get lumped on the end of the list for the ICB
<emeyer> iank_: The difference at the moment is Gecko doesn't do this well
<emeyer> iank_: In Gecko the placeholder isn't reparented so you need to lay out a second level of top layer thing to get the correct static position
<emeyer> fantasai: They're both top layer?
<emeyer> iank_: They're both direct descendants of the ICB
<emeyer> fantasai: I agree with Emilio it's super weird to reparent them and have them at the end of the static HTML element
<emeyer> fantasai: I suspect what the spec meant is similar to what happens with fixed position
<emeyer> fantasai: They wanted to make sure it would be painted on top
<emeyer> iank_: Pulling into a separate list at the ICB level achieves what's needed
<emeyer> fantasai: Yeah, the answer is just very weird
<futhark> q+
<emeyer> …I agree with Emilio that the static position of the element being at the end of the HTML element is super weird even if that's easy or what the spec currently says
<miriam> ack futhark
<fantasai> s/super weird/super weird from an author's PoV/
<emilio> https://drafts.csswg.org/css-position-4/#top-styling
<emilio> > Unless overridden by another specification, its static position for left, right, and top is zero.
<emeyer> futhark: In section 3.1 in CSS-position-4, there's a list on top layer styling that the static position for left, right, and top is 0.
<emeyer> TabAtkins: I guarantee I forgot to list out all four
<emeyer> fantasai: I don't know how much review this rewrite has gotten
<emilio> Comes from https://github.com/w3c/csswg-drafts/commit/051d37a64ca714fa0238df6db019967de2a086dd fwiw
<emeyer> fantasai: I'm not sure how much reliance I'd put on the validity of the spec since it was copied over from another spec
<emeyer> TabAtkins: Since top layer is new, we probably don't have a lot in the way of meaningful compatibility troubles
<fantasai> s/another spec/another spec drafted by someone who isn't a CSS layout spec expert/
<emeyer> …So let's just do the simplest possible thing!
<emeyer> …Which is 0,0 static position
<emeyer> iank_: With a zero-width-height static position?
<emeyer> TabAtkins: The concept of static position doesn't exist and everything resolves to zero
<emeyer> iank_: You don't want everything to resolve to zero
<emeyer> TabAtkins: Why not?
<emeyer> iank_: I think you actually just want the static position at 0,0
<emeyer> TabAtkins: I don't understand the objection
<emeyer> iank_: Anchor position sets the alignment differently and strethces
<emeyer> TabAtkins: Oh, yes, we probably want to fix that to not stretch
<emeyer> iank_: Other elements don't support alignment
<emeyer> TabAtkins: If we set it to 0,0 in the corner we don't get the auto-alignment, which we wanted to avoid in anchor positioning
<emeyer> …For legacy reasons, we can't get away with that in abspos
<emeyer> iank_: I think in UA styles, all these things have been set to zero anyway
<emeyer> …Like, you have to set insets to trigger this behavior, right?
<emeyer> emilio: For popover, I think so, but I'm not sure about other things
<emeyer> iank_: I'm fine with saying that static positioning is zeroed out
<emeyer> fantasai: Is that going to make sense for non-fixed-position cases like top layer elements that are halfway down the page?
<emeyer> iank_: Reparenting in the box tree up to the very top, when would that trigger?
<emeyer> fantasai: You scroll halfway down the page and you click on something that's in the top layer
<emeyer> …If it's fixedpos and not in the viewport you can't read the whole thing
<emeyer> …Sometimes it gets pushed into a static position, which makes it scrollable
<emeyer> TabAtkins: The generic answer is, if you want to attach to a thing on the page, use anchor positioning
<emeyer> …We don't need to rely on fixed things that are weird if we have a more powerful variant
<emeyer> (awkward silence)
<emeyer> miriam: I don't feel a need to preserve static positioning generally, and especially not going into the top layer
<emeyer> TabAtkins: Can we just say that then?
<emeyer> …If you're top layer, we do the same as anchor positioning?
<emeyer> fantasai: I think what makes more sense is to define the static position rectangle in the ICB, which I think gives you the right effects
<emeyer> TabAtkins: I don't see why it would, necessarily
<emeyer> …There'd be no connection to its normal position in the DOM
<emeyer> fantasai: I think we want to define the rectangle where it lives and define what that means
<emeyer> …If you set everything to zero then it will anchor to that edge
<emeyer> …We should talk about it offline
<emeyer> TabAtkins: Ah, I see what you mean
<emeyer> …We can resolve on doing something arbitrary
<emeyer> fantasai: Propose to make the static position of the top layer does not depend on the size or position of any other element on the page
<fantasai> fantasai: And probably define by making staticpos rectangle as ICB
<emeyer> miriam: Do we have a thing we can resolve on?
<fantasai> PROPOSED: Make the static position of top layer boxes not depend on the size or position of any other element on the page, likely by making static position rectangle the ICB
<emilio> sgtm
<emeyer> miriam: Any objections?
<fantasai> RESOLVED: Make the static position of top layer boxes not depend on the size or position of any other element on the page, likely by making static position rectangle the ICB
<RRSAgent> I have made the request to generate https://www.w3.org/2024/06/11-css-minutes.html fantasai
<jarhar> scribenick: jarhar

css-meeting-bot avatar Jun 11 '24 14:06 css-meeting-bot

@emilio I put in some minimal edits to define the ICB as the static position rectangle for top layer elements. Is there anything else you think needs clarification here? https://drafts.csswg.org/css-position/#staticpos-rect

fantasai avatar Aug 09 '24 23:08 fantasai

@fantasai looks good!

emilio avatar Oct 25 '24 13:10 emilio