w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.46k stars 657 forks source link

[css-anchor-position] Absolutely positioned element does not find fixed anchor #10419

Closed RWDavid closed 1 month ago

RWDavid commented 3 months ago

I am currently creating small test cases to solidify my understanding of the anchor positioning spec.

Here is one such test case that utilizes a position: fixed anchor and a position: absolute anchored element.

<style>
  .anchor {
    anchor-name: --anchor-el;
    position: fixed;

    background: blue;
    width: 150px;
    height: 150px;
    border: 2px solid white;
  }

  .anchee {
    position: absolute;
    position-anchor: --anchor-el;
    inset-area: end;

    width: 100px;
    height: 100px;
    background: red;
    border: 2px solid white;
  }
</style>

<div style="position: relative;">
    <div class="anchor"></div>  
    <div class="anchee"></div>
</div>

It seems like the anchor is an acceptable anchor element, but the anchored element does not seem to accept that element as an anchor (at least on Chrome Canary version 127.0.6532.0).

nt1m commented 3 months ago

@mfreed7 @lilles @tabatkins Do you know if this is a spec issue or an implementation issue or neither?

yisibl commented 3 months ago

As far as I know, the current specification does not allow anchoring to fixed-positioned elements.

But this is indeed a common use case, such as the example in the floating UI.

https://github.com/w3c/csswg-drafts/assets/2784308/f58293b7-b41a-4530-ae58-e1e2f97f0566

RWDavid commented 3 months ago

The spec actually does permit anchoring to fixed-position elements. Although the exact wording is "absolutely positioned", both position: absolute and position: fixed are considered "absolute positioning".

This behavior works in Chrome, which you can see by replacing the position of the anchored element in the above test case from absolute to fixed.

yisibl commented 3 months ago

@RWDavid See https://github.com/w3c/csswg-drafts/issues/8583#issuecomment-1470927460

mfreed7 commented 3 months ago

So this is correct behavior, per spec. Your “anchee“ element is absolutely positioned inside the position:relative containing block. Your "anchor" is fixed positioned, so its containing block is the ICB.

And https://drafts.csswg.org/css-anchor-position-1/#acceptable-anchor-element says:

Here, el is "anchor" and query el is "anchee". And this point isn't true, because "anchor" is absolutely positioned (fixed counts as absolutely positioned), and "anchee" comes after "anchor" in the layout tree, again because "anchee" is fixed positioned. So "anchor" is not an acceptable anchor element for "anchee". As mentioned above, change "anchee" to position:fixed and you'll be good. Or remove position:relative from the wrapper, so both have the same ICB containing block.

RWDavid commented 3 months ago

I am a little confused. Isn't my anchor element fixed positioned and isn't my positioned element absolutely positioned? And also, isn't my el is indeed placed before query el in the tree order?

mfreed7 commented 3 months ago

Sorry, I got the anchor/anchee backwards in the first paragraph. Fixed now. The logic still holds. The anchor comes after the anchee in the layout tree. The DOM tree order is not material here.

yisibl commented 3 months ago

@mfreed7 As far as the implementation in Chrome is concerned, is it possible to anchor to a fixed element?

I simulated the effect of Floating UI by container query, but there is jitter when scrolling fast. https://codepen.io/yisi/pen/mdYExKX?editors=0100

bfgeek commented 3 months ago

It is possible to anchor to a fixed element if it is laid out before the other abs/fixed pos element.

E.g. if the fixedpos is captured by a containing block (that's not the ICB) with a transform or similar effect. Or referencing a another fixedpos element if they are both in the ICB.

tabatkins commented 3 months ago

Yes, this is working as intended. Your fixpos successfully finds the viewport as its containing block, while the abspos uses its parent div. In order for something to be a valid anchor, it has to be below the positioned element's CB in the box tree, but the viewport is considered "above" everything in the document.

Generally, a fixpos will only be able to serve as an anchor to other fixposes (and they'll have to follow it in DOM order). There's exceptions if an ancestor generates a fixpos containing block (like an ancestor with transform), but that's a relatively rare case (it makes the element act like an abspos, so usually is something you avoid).

fantasai commented 3 months ago

Your "anchor" is fixed positioned, so its containing block is the ICB.

@mfreed7 This is not true. The containing block of a fixed positioned box is the viewport (which does not scroll with the canvas), not the ICB (which does).

And this point isn't true, because "anchor" is absolutely positioned (fixed counts as absolutely positioned), and "anchee" comes after "anchor" in the layout tree, again because "anchee" is fixed positioned.

Being fixedpos doesn't change a box's position in the box tree.

Note, the fixedpos in the OP's testcase matches this condition here:

Either el is a descendant of query el’s containing block, or query el’s containing block is the initial containing block.

by virtue of being a descendant of it and the abspos's mutual parent, not by having a containing block of the ICB. If the fixedpos were a sibling of the relpos box then it would fail both conditions. I'm pretty sure that's not intended.

The prose in this section is just awfully hard to read; if nothing else this issue should stay open for editorial improvements.

Westbrook commented 2 months ago

I'm running into a similar issue with regards to "absolutely positioned" content.

See this broken example wherein:

You can work around this by positioning the parent differently, as seen here:

Do we see this as "outside of the spec", something yet to be covered, an implementation issue? I've opened a Chromium bug in case it is the later...

tabatkins commented 1 month ago

@Westbrook Hm, that example definitely looks broken to me. Reproducing it myself, I find that it's the specific combination of (1) the container being abspos or fixpos, (2) the anchor being fixpos, and (3) the dialog being in the top layer, that break the positioning. Change any of those three, and it works successfully. This suggests a bug.

I think it's caused by the "acceptable anchor element" definition not really mentioning top layer stuff. I'm considering rewriting the relevant ordering conditions to the following:

* |possible anchor| is painted strictly before |positioned el|,
    aka one of the following is true:
    * |positioned el| is [=in a higher top layer=] than |possible anchor|
    * Both elements are [=in the same top layer=]
        but have different [=containing blocks=], 
        and |positioned el|'s [=containing block=]
        is an ancestor of |possible anchor|'s [=containing block=]
        in the [=containing block chain=],
        aka one of the following:
        * |positioned el|'s [=containing block=] is the viewport,
            and |possible anchor|'s [=containing block=] isn't.
        * |positioned el|'s [=containing block=] is the [=initial containing block=],
            and |possible anchor|'s [=containing block=] is generated by an element,
        * both elements' [=containing blocks=] are generated by elements,
            and |positioned el|'s containing block
            is an ancestor in the [=flat tree=]
            to that of |possible anchor|'s [=containing block=].
    * Both elements are [=in the same top layer=]
        and have the same [=containing block=],
        and are both [=absolutely positioned=],
        and |possible anchor| is earlier in [=flat tree=] order
        than |positioned el|.
    * Both elements are [=in the same top layer=]
        and have the same [=containing block=],
        but |possible anchor| isn't [=absolutely positioned=].

(The constraints that aren't about relative ordering stay as they are.)

@lilles or @bfgeek , do these revised conditions sound correct to you? @fantasai, do these read better than the current text?

tabatkins commented 1 month ago

Lol, the bug you opened got fixed yesterday, and the example now works. Guess that's confirmation. ^_^