Closed tabatkins closed 2 years ago
This negates the entire purpose of position fallback
Not the entire purpose - just for that specific use-case. Its still useful when no scrollers are present.
There are two potential solution spaces:
1) Part of the design initially was to snapshot the scroller positions (at some well defined time - previously this was when you would call the popup() method or similar), and store them on the DOM tree. With this you can apply adjustments to the anchor positions. However it will not keep in sync with any scrolls. This is akin to doing something like:
top: calc(anchor(top --a1) + var(--scroll-top-passed-in-by-script));
The "well defined time" needs to be super well defined - it likely needs to be a script API. We can't do it during layout as there are too many random things that will update layout, leaving this inconsistent between browsers.
2) Remove the layout part of fallback positioning (you just get a one-shot), then a different syntax for fallback positioning which doesn't use layout properties - rather describes the position adjustments for example (e.g. can do this based on anchor-scroll , then a separate compositor time adjustments to be performed). No layout however.
Ian
I like option 2 better because that keeps the pipeline sane -- no layout depending on scrolling.
With the option 2, we can re-position but we can't resize, can we?
If resizing is not an important scenario, the option 2 looks reasonable to me. We'll need to clarify the behavior for e.g.
#myPopup {
position-fallback: --button-popup;
right: anchor(--icb right);
min-width: 6em;
}
@position-fallback --button-popup {
@try {
left: calc(anchor(--button right) + 1em);
}
@try {
left: anchor(--button right);
}
}
Then the 2nd try should ignore the right
property?
I just recalled that we already have a similar "pipeline violation": @scroll-timeline
, which makes animation progress depend on scroll offsets.
So if involving scroll offsets in style isn't a big issue, then maybe it's not a big issue for layout, either?
@andruud
(Only 18 days later):
Yes, @scroll-timeline
(now scroll-timeline
) has/had a similar problem.
A ScrollTimeline has an internal state which contains the scroll position of the associated scroller. We generally snapshot this state once during the lifecycle (before style and layout), but also immediately after the ScrollTimeline is created.
However, snapshotting a newly created ScrollTimeline triggered by style resolution isn't effective, since we haven't done layout yet (we don't even have a layout box), and we have no useful scroll position. In other words, the scroll-animation has no effect that frame, and we get a flash of "unstyled" content. (AKA the first frame problem, also previously discussed in the context of css-toggle).
This was solved (in Blink, never specified) by doing the following:
blink::DocumentAnimations::unvalidated_timelines_
).blink::LocalFrameView::RunScrollTimelineSteps
).But the current solution has some quirks:
Some of that quirkiness was intentional at the time ... but I don't remember all the reasoning anymore, and now they just look like problems that should be fixed.
Had a discussion today, and I realized that the current version (including scroll offsets in anchor()
evaluation) makes it almost impossible to do composited scrolling, as elements can now arbitarily resize when scrolled. So I'm proposing a new version.
anchor-scroll
propertyel
whose anchor-scroll
resolves to element --anchor
, calculate the accumulated scroll offset of all the scroll ancestors of --anchor
element up to the containing block of el
el
as the aboveanchor()
is the same as the old version, namely, without involving scroll offsets at allThis also allows composited scrolling as long as the fallback position doesn't change. The compositor needs to keep track of the scroll containers up to the containing box, same as in the current implementation.
To react to fallback position changes, we just let the main thread invalidate it, because compositor can't handle it properly.
This proposal is what I got after soring out what I think our goals and non-goals are:
Goals:
Non-goals:
anchor()
, not because there are strong use casesAll right, revived 'anchor-scroll' along the lines that Xiaocheng listed above. Haven't put in precise timing details because I'm finishing this off while in vacation transit and don't want to go investigate that hard, but I left an issue reminding me to fix that. ^_^
While chatting with @xiaochengh, we suddenly realized that, as far as we can tell, position fallback is actually completely worthless as specified. (Quick reminder - we lay out the element as specified, and if it overflows, swap some properties for the first fallback set and try again.)
As currently implemented (and specced, I guess), this overflow calculation is based on the element's scrollable ancestors (including the root scroller?) being at the initial scroll position. This negates the entire purpose of position fallback, which is to let you, say, have a
select
-like dropdown that positions below the anchor element if possible, or above if the anchor is too low in the page. As implemented it'll only concern itself with whether the anchor element is too close to the bottom of its parent element, not whether it's too close to the bottom of the screen itself, based on the current scroll position.Are we missing something? Is this actually fine and allowed in the theoretical model, and we just need to clarify something in the spec? Or are we screwed, and need to adopt a totally different model that can just do fallback position but not change layout in any way, so it can be done in the compositor?
/cc @bfgeek @kojiishi