Skip to content

[css-anchor-position-1] position-visibility: anchors-visible should be clear about when is visibility determined #12732

Closed

Description

@emilio

https://drafts.csswg.org/css-anchor-position-1/#valdef-position-visibility-anchors-visible says:

If the box has a default anchor box but that anchor box is invisible or clipped by intervening boxes, the box’s visibility property computes to force-hidden.

There are multiple problems with that definition:

  • Is the box's visibility property really intended to compute to force-hidden? There's no test for this, and I think it would make more sense to be a used-value time thing. In fact, at least that's how WebKit implements it (and Blink probably, too, given there's no test for force-hidden at all in all of WPT). Computes to would imply that you need to update the style in the whole subtree, because visibility is inherited, which seems rather unfortunate. It also has bigger ramifications (affects style container queries and so on...).

  • There's nothing that says when the visibility is checked. I'm assume, given the definition, the idea is that you check it every layout? If so I think that makes an even stronger case for it being used-value time tbh...

I guess the idea is that #10182 allowed you to trigger enter / exit transitions (not clear to me how... I guess transitioning position-visibility + container-queries or something)?

Maybe a more sensible approach is to use the same timing we use to evaluate fallbacks (ResizeObserver timing)?

cc @fantasai @tabatkins @dshin-moz

Activity

  1. emilio commented on Sep 4, 2025

    @emilio
    CollaboratorAuthor
  2. emilio commented on Sep 4, 2025

    @emilio
    CollaboratorAuthor

    If we don't want to change the timing here (which btw I think would allow for more interesting behavior, such as proper IntersectionObserver-like visibility testing), I think at least a note in the spec about it being evaluated every layout would be nice, since it came as a surprise to me (when I first read about this feature, I assumed it would use the existing ResizeObserver-timed mechanics).

  3. progers commented on Sep 4, 2025

    @progers
    Contributor

    For what it's worth, Chromium's implementation uses our intersection observer code and runs it just prior to resize observer.

  4. emilio commented on Sep 4, 2025

    @emilio
    CollaboratorAuthor

    Oh! Interesting. I think that's the sensible thing to do... But that looks nothing like what the spec says, since then presumably it also supports things like clip-path etc, and all the scrollers all the way to the root, right? (apart from the timing difference being observable of course)

  5. emilio commented on Sep 24, 2025

    @emilio
    CollaboratorAuthor

    @tabatkins I see you landed a spec edit for this. Was that intentional? if so it should probably use the concepts from whatwg/html#11613? I assume Blink uses the same mechanism and not something weirder?

  6. css-meeting-bot commented on Sep 24, 2025

    @css-meeting-bot
    Member

    The CSS Working Group just discussed [css-anchor-position-1] position-visibility: anchors-visible should be clear about when is visibility determined.

    The full IRC log of that discussion <TabAtkins> emilio: the spec doesn't say when the anchor's visibility is checked
    <TabAtkins> emilio: and when I was looking to implement, I realized the spec technically allows....
    <TabAtkins> emilio: if it did something more complicated you could only do it as post layout
    <TabAtkins> emilio: but the clipping thing specified is at layout time
    <TabAtkins> emilio: the issue is about defining *when*
    <TabAtkins> emilio: webkit does it during layout. blink does some intersection observer thing, which is a bit more flexible
    <TabAtkins> emilio: but might be more confusing
    <TabAtkins> emilio: if you change layout in a way that overflows, then elementFromPoint(), whether that hit test succeeds or not depends on timing
    <TabAtkins> emilio: no strong opinion on what happens, just want it specified
    <TabAtkins> emilio: I think Tab added an edit
    <TabAtkins> emilio: should check with WebKit. and spec edit should use some of the post-layout things Rune is doing.
    <futhark> q+
    <TabAtkins> TabAtkins: I specified "just before ResizeObserver", because that's what Philip said and Emilio agreed. More details would be great.
    <TabAtkins> emilio: it needs to be a bit more detailed, because ResizeObserver loop is more stuff - content-visibility, scroll size observer... it needs to be specced in amore detailed way. rune can give details
    <astearns> ack futhark
    <TabAtkins> futhark: sorry, don't have specific context on position-visibliity, but it does make sense to happen at same time
    <TabAtkins> futhark: also on whether rpost-layout snapshot happens before or after resizeobserver
    <TabAtkins> futhark: ties in, I agree
    <TabAtkins> fantasai: I pinged antii to see if he has feedback. I have no idea how this timing fits in with anything
    <fantasai> s/antii/antti/
    <TabAtkins> fantasai: I just want authors to have something that generally works. not sure to what extent we care about exact nuances beyond that
    <emilio> q+
    <TabAtkins> fantasai: so how much of this is about authors caring about timing, and how much is about writing WPTs
    <TabAtkins> fantasai: so long as the author-observable behavior is something they're happy about, and we have interop on aspects that we need *for authors*
    <astearns> ack emilio
    <TabAtkins> emilio: not about locking things down. timing issue ends up... if we dont' define it, we'll hit compat issues.
    <TabAtkins> emilio: it's pretty different observable behavior. if you do it before resize, then layout changes from an RO won't hide an anchor until next frame
    <TabAtkins> emilio: on flip side, if you do it during layout, that's most CSS-y it's not really observable, but it's less flexible. can't check for intersection with other elements.
    <TabAtkins> emilio: i'm assuming blink is actually reusing Intersection observer, which does a lot more than just checking scroll overflow
    <TabAtkins> emilio: that's a big behavior different, should use one or the other
    <TabAtkins> emilio: IO uses overflow-clip, o-c-margin, etc
    <TabAtkins> emilio: and those are probably something the author would expect to work
    <TabAtkins> fantasai: yeah, defining whether we check clip is definitely defined
    <TabAtkins> TabAtkins: I think that's already defined
    <fantasai> TabAtkins: We should define behavior we want
    <TabAtkins> emilio: right now I think spec is just overflow based
    <TabAtkins> astearns: so how to resolve on this?
    <TabAtkins> emilio: if we want to check IO things, we need to define a fixed point in the frame timing
    <TabAtkins> fantasai: I think we should just be checking for what's in the spec right now
    <TabAtkins> fantasai: if we did clip path and other stuff.... I don't think tha't snecessarily what authors would expect
    <TabAtkins> emilio: if you did this as an author you'd do IO
    <TabAtkins> fantasai: sure but i'm not convinced it's really what you *want*
    <TabAtkins> fantasai: so we have some qs to investigate. what do we think authors expect about clip behavior and anchor visibility? and the timing question
    <TabAtkins> fantasai: which depends on that first q
    <TabAtkins> emilio: I think that makes sense
    <fantasai> s/first q/first question, so let's answer that one first/
    <TabAtkins> emilio: I think spec currently only does check from the anchor to the CB of the positioned El, and that's another big change from IO
    <TabAtkins> fantasai: right, I think IO isn't what we want to do
    <TabAtkins> emilio: maybe. I think it's weird, for example, to not intersect with the viewport.
    <TabAtkins> astearns: take back to the issue, then?
    <TabAtkins> fantasai: yes
  7. fantasai commented on Sep 24, 2025

    @fantasai
    Collaborator

    So the two questions that came out of the call were:

    • What types of clipping should position-visibility be checking for?
    • When should it be calculated? (This is somewhat dependent on the first question.)

    For the first question, it would be useful to hear from anchorpos users. Currently the spec only checks for overflow clipping between the anchor and the abspos CB. It doesn't check for visibility in the viewport or clip-path or anything else. Personally I think this is the right answer.

    For the second question, WebKit re-evaluates after each layout, which means that there's no lagging across frames. We think this is better behavior for users, and would prefer if it were allowed by the spec.

  8. chrishtr commented on Sep 29, 2025

    @chrishtr
    Contributor

    TL;DR: I propose that we resolve no change to the existing WD spec, which already specifies overflow clips up to the abspos CB, and also the timing of visibility checks. I think this already agrees with implementation behavior for both Chrome and Safari.

    What types of clipping should position-visibility be checking for?

    I agree that overflow clips between the anchor element and the abspos CB should be taken into account, but not the viewport. The reason is that, if the anchored element looks to the user like it's part of a subscroller, then it doesn't make sense to the user what it's anchoring to if that anchor point is not even visible on the screen.

    Example here (observe what happens when you scroll further up or down in that scroller which has a tooltip for "Item 6"). (Note: this demo is from @una who is close to the developer community.)

    The browser viewport doesn't have this problem because its clip always applies to everything--including anchored elements--and so there is no point to doing it there because it will have no effect. But for a subscroller, the anchored element escapes the clip of the subscroller because it's out of flow.

    I also don't think other clips need to be taken into account, because it's just a scrolling effect due to the user agent controlling scrolling, not any other visual effect.

    When should it be calculated?

    I agree that visibility should be re-evaluated after every layout (*) so that there is no lagging across frames.

    From what @fantasai said, I think the WebKit and Blink implementations actually already match.

    Blink does position-visibility hiding computation updates after layout and before JS ResizeObservers (but it is also part of the loop between them, so if a ResizeObserver dirties layout then visibility will be re-calculated after the subsequent layout).

    Implementation note: The intersection observation generated for position-visibility by Blink is also delivered during that same frame. We do this with a special kind of Blink-internal sync observation not available to JS-generated IntersectionObservers.

    (*) Or more more precisely, every layout that is part of the update-the-rendering/animation frame generation steps.

  9. chrishtr commented on Sep 29, 2025

    @chrishtr
    Contributor

    I'd like to discuss this and hopefully resolve as a high priority item this week, since it affects Interop 2025.

  10. emilio commented on Sep 29, 2025

    @emilio
    CollaboratorAuthor

    I think this already agrees with implementation behavior for both Chrome and Safari.

    It doesn't? Safari does it during the style / layout loop afaict.

    Also, Blink afaict just has the behavior of IntersectionObserver with root: <the closest ancestor to the containing-block>. Let me know if I'm misreading the code.

    But that's not what the spec says to do (that accounts for a lot more than scrolling overflow, like overflow: clip, clip-path, etc).

  11. 28 remaining items

  12. fantasai commented on Nov 5, 2025

    @fantasai
    Collaborator

    Ok, we committed the following text:

    Whether or not anchor is clipped by intervening boxes must be checked after updating content relevancy for a document (see content-visibility in [css-contain-2]) and before running any IntersectionObserver, and may also be checked at other times to improve responsiveness.

    @emilio @andruud @anttijk Can you verify whether this matches your expectations on what this is supposed to say? Tab and I aren't very clear on how all the timing details stack.

  13. emilio commented on Nov 5, 2025

    @emilio
    CollaboratorAuthor

    I think the right fix would be to expose an algorithm or hook to check the intervening boxes and call it somewhere from steps 16/17 of https://html.spec.whatwg.org/#update-the-rendering. The version of anchors-visible that browsers implement can probably run once after the loop (so would become step 17), but for the version that actually changes the computed value of visibility that's trickier.

  14. emilio commented on Nov 5, 2025

    @emilio
    CollaboratorAuthor

    If we decide to go for tweaking styles or what not, reusing the same steps whatwg/html#11613 is introducing probably makes more sense.

  15. emilio commented on Nov 5, 2025

    @emilio
    CollaboratorAuthor

    cc @lilles for the above :)

  16. fantasai commented on Nov 5, 2025

    @fantasai
    Collaborator

    Ok, updated to the following for now:

    Whether or not anchor is clipped by intervening boxes must be checked after updating content relevancy for a document (see content-visibility in [css-contain-2]) and running any ResizeObserver, but before running any IntersectionObserver. It may also be checked at other times to improve responsiveness.

    Lmk if this is good enough.

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