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-anchor-position-1] Using a specific box (not your CB) to determine position-fallback #8724

Closed
tabatkins opened this issue Apr 14, 2023 · 3 comments

Comments

@tabatkins
Copy link
Member

@una brought this case to our attention today: if you use abspos+anchor() to position an element, position fallback uses the abspos containing block (the nearest relpos ancestor) to tell if the positioned element is overflowing and should try a different position. But if you use fixpos or popover, which are otherwise strictly more powerful for anchor() purposes, you lose this ability - the CB is now the ICB, and you can't reference anything else.

The problem here is, partially, that the inset properties perform double duty - they adjust the size of the inset-modified containing block and position/size the element within the IMCB. Anchoring needs to use them for the latter, but position fallback still needs the former as well.

I suggest that we add a property allowing you to specify a particular anchor to use as your position-fallback overflow bounds, rather than your normal CB. Maybe:

position-fallback-bounds: normal | <<dashed-ident>>;

normal means to use your positioning CB, as specified today. Giving a dashed-ident performs anchor selection (same as what's required for anchor()), and uses the resulting element instead.

(In theory one might want to use different elements for each side, or slightly adjust the bounds inward or outward from the bounding element's edge. This grammar is extensible in the future to a four-edge version, if we'd like.)

/cc @xiaochengh

@tabatkins
Copy link
Member Author

Xiaocheng brought up that we need to invoke scroll-adjustment for this rectangle, same as we do for the IMCB. If the named element is being scrolled by some ancestor, you still want to track it's current (scrolled) position, like you would have automatically done back when you were an abspos descendant of it and all the calculations were local.

A further concern was what to do about inset-modified stuff. In part, the reason we use IMCB is to avoid accidentally overlapping the anchor - for example, if you have left: 0; right: anchor(left);, you expect to fit into the space between the left edge of your CB and the anchor itself. If you end up wider than that, you want to trigger fallback and try another position, not happily overflow your anchor because you haven't overflowed your CB itself.

After some discussion, we've decided that the simplest answer that is also likely to be correct is to just intersect the two rectangles - your IMCB and the fallback-bounds rect. In the common case, your fallback-bounds rect will be a subrect of your CB, so intersecting with your IMCB will still generally produce a useful rectangle.

In some cases this might produce a significant behavior difference between "the rect you tested against as an abspos" and "the rect you're testing against as a fixpos". For example, with left: 20px; right: anchor(left);, as an abspos you'll be fitting into a rect inset 20px from your CB's left edge, and flush with your anchor's left edge. If you become a fixpos, that 20px is inset from the screen edge instead, which might be far away from your fallback-bounds rect; when you intersect the two, you'll instead get a rect that's flush with the fallback-bounds rect's left edge, rather than 20px away from that edge.

However, if this is important you can always correct it by rewriting your insets to refer to the fallback-bounds rect; by definition it has to be a valid anchor as well. So when you go from abspos to fixpos, you can also change from left: 20px; to left: calc(anchor(--old-cb left) + 20px); and maintain identical bounds. Since most of the time it'll work reasonably by default, and it's fixable without much effort, I think going with the simple "just intersect them" is the right answer.

@xiaochengh
Copy link
Contributor

What if the position-fallback-bounds element's box is fragmented?

The current Chromium implementation can only use the axis-aligned bounding rect as the bounding box. And it would be super complicated to use the union of the fragments instead (which looks a bit more natural I guess?), so I don't think this is going to happen.

Anyway, I don't think this is an important case. I think we can keep using the axis-aligned bounding rect for now, just for the sake of spec completeness.

(plus we haven't figured out how to do position fallback with fragmented containing block yet...)

@tabatkins
Copy link
Member Author

Closing as dupe now of #9868

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

2 participants