w3c / csswg-drafts

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

[css-anchor-1] Need ability to say "don't render" when anchor is off-screen #7758

Closed tabatkins closed 5 months ago

tabatkins commented 1 year ago

Consider: an anchor is inside of a scroll container. The positioned element is outside of the scroll container. This is legit if you want the positioned element, when visible, to be able to project outside of the scrollable area, such as into the margin.

However, this then means that if the anchor is scrolled outside of the scrollport, the positioned element just hovers above or below the scroll container, wherever the anchor is theoretically positioned. That is almost certainly not what you want!

So we should have some way to specify that if the anchor is off-screen, the positioned element should hide itself.

Questions:

tbondwilkinson commented 1 year ago

I think JUST add display:none could be jarring as anchors scroll in and out of the visible content.

From an Open UI perspective for popup=hint, it might be better to trigger a hide event when anchors scroll out of the visible content, rather than hiding and reshowing.

It might be hard to guess what a good default would be for this, hiding is not a bad idea.

Another thought is that maybe sometimes people will want the anchored element to be clipped by the scroll container rather than hidden?

tabatkins commented 1 year ago

I think JUST add display:none could be jarring as anchors scroll in and out of the visible content.

It could be, or could not be, depending! If your anchors purposely hang out outside of the scroller, tho, you've gotta do something when the anchor is out of the viewport.

From an Open UI perspective for popup=hint, it might be better to trigger a hide event when anchors scroll out of the visible content, rather than hiding and reshowing.

I haven't dug deeply enough into OpenUI - can you give me a pointer to hide event stuff?

Another thought is that maybe sometimes people will want the anchored element to be clipped by the scroll container rather than hidden?

You can achieve this by putting the positioned element in the same scroller as the anchor.

clshortfuse commented 1 year ago

Just saw the blog on this and was concerned about this, watching them overlayed outside the container. My custom anchor system uses IntersectionObserver:

https://github.com/clshortfuse/materialdesignweb/blob/d0e11f0f1e27e2da19d60e8f9386cf2e4e28bf9a/mixins/TooltipTriggerMixin.js#L61-L90

this is the the anchor. If the anchor is less than 50% visible, I close the tooltip. Automatic handling is neat, but I think IntersectionObserver would handle it. Now if we're saying over CSS, then that's something else...

Edit: Newer commit

xiaochengh commented 1 year ago

I have a proposal that's built on top of #8724, which if resolved will allow us to specify a bounding box (not just the CB in positioned layout) for position fallback.

Then we can have a new property that controls what to do when the anchored element ends up overflowing that bounding box:

position-fallback-overflow: clip | visible

clip means the anchored element should be clipped by the bounding box. visible means no such clipping.

The initial value is clip.


Discussion:

  1. I think using the position fallback bounding box is more natural than checking whether the anchor is off screen (or off any box). And it doesn't have any issue with multiple anchors.

  2. Clipping seems to provide a better/smoother UX than hiding & showing. User probably don't want to see the element suddenly disappear when something is just slightly off screen.

  3. The UX can be even smoother if we have #8200

  4. Clipping seems more friendly to the rendering pipeline than hiding/showing, because we are just introducing a clip box.

  5. Agreed that it should be activated by default. But it might be just safer to also provide an opt-out. (and it also means I don't need to massively rewrite all the existing WPT)

  6. I don't think it should fire any event on its own. IntersectionObserver should be good enough, at least for now.

tabatkins commented 1 year ago

This is an excellent argument, and a great idea. Yeah, I think being able to clip to the box that you're using for overflow bounds sounds great.

Not sure we should default to clip, tho - allowing the element to extend out of the element by default seems fine. Like, a tooltip should be able to poke out a bit. In particular, I think that the behavior if you get it wrong is better if we default to visible: if you mean to clip and forget it's probably pretty obvious in common cases (you'll have things floating in space randomly on the page), while if we default to clip and you mean to use visible that's only obvious if the element happens to get positioned near the edge so it overflow a little bit. Plus in general it better fits our "fail visible/open" policy in CSS - you should usually design defaults so that if the author doesn't think about it the content is still visible, even if it makes the page ugly.

Okay, so I'm inclined to just go ahead and edit in #8724 and this one. I'll review #8200 as well to see if I want to go ahead and pull it in, too.

tabatkins commented 1 year ago

Or hm, maybe we should act as 'clip' if you have a specified -fallback-bound element, and visible if you don't. So the initial value should be an 'auto'.

xiaochengh commented 1 year ago

I found a real use case for the visible value: https://floating-ui.com/docs/autoPlacement

We can achieve the same by:

clshortfuse commented 1 year ago

Would visible also allow replicating this?

https://github.com/w3c/csswg-drafts/assets/9271155/0360aa7b-b4e8-4c37-a0c5-341cd353c3dc

xiaochengh commented 1 year ago

@clshortfuse

This looks like a different thing, which is closer to Floating UI's shift that tries to keep the element in the bounding box without switching position; while this issue is about what to do when it's overflowing the bounding box.

And we don't have full support for shift yet. I have a hacky demo, but it doesn't work with position: fixed, so it probably doesn't work for popovers.

tabatkins commented 1 year ago

Thinking about this more as I try to write it up - rather than clipping to the overflow box, would it make more sense for the default to be "clip to the bounds of every scroller between you and the specified anchor"?

In particular, if there are two scrollers between your CB and your anchor, and the innermost has the anchor in the scrollport but the outer has the entire inner scroller out of the scrollport, you still want to clip, I assume. If we rely on just the overflow box, even if you specify an additional one with anchor-overflow-bounds, it won't be able to capture both boxes.

xiaochengh commented 1 year ago

I feel like we may still want some consistency between here and #8724. If we use a box for overflow clipping, we would very likely also want to use the same box to trigger position fallback.

Then everything gets really complicated if we need to consider a chain of clipping boxes. Imagine if someone wants to use the intersection of the ancestor scrollers of the anchor to trigger position fallback...

I don't have a good idea towards this direction. Some thoughts are:

tabatkins commented 1 year ago

If we use a box for overflow clipping, we would very likely also want to use the same box to trigger position fallback.

Yeah, I definitely agree. I'm talking about an additional clip - in addition (and maybe by default?) clip to the scrollers between the anchor-scroll element and them as well. Multiple values for the clipping property (which we should move to an anchor-* name rather than position-fallback-* name, probably anchor-clip).

Like, anchor-clip: none | [ fallback || scroll ]? Meaning "clip to the position-fallback-bounds elements and/or the scroll containers of the scroll-anchor element". Initial value scroll? Or maybe none, hm.

Imagine if someone wants to use the intersection of the ancestor scrollers of the anchor to trigger position fallback...

I don't think fallback with the scrollers is necessary; the intermediate scrollers don't have any particular reason to be layout-relevant to the positioned element, they're just ways to hide the anchor and thus things that the positioned element probably wants to be hidden by as well.

xiaochengh commented 1 year ago

anchor-clip: none | [ fallback || scroll ] SGTM

xiaochengh commented 1 year ago

@wangxianzhu

I'd like to get your input here. Do you think anchor-clip: scroll is implementable?

In particular, for each anchor-positioned element, we need to duplicate a bunch of clip nodes from the ancestor scrollers of the anchor.

xiaochengh commented 1 year ago

Discussed with @wangxianzhu offline. This should be implementable in Chromium

The solution doesn't involve duplicating any clip nodes, but we can simply make PaintPropertyTreeBuilder switch to the scroller's clip state for the target element, so there's no performance issues either.

xiaochengh commented 1 year ago

A slightly revised idea: anchor-clip: auto | none, where

There's a possibility that in the future, this gets extended into anchor-clip: auto | none | fallback | <anchor-element>, meaning this element should be clipped by all ancestor clip boxes of the target element of the value, and the impl should be pretty similar. Or even extended into a multi-value syntax. But everything feels like YAGNI at least for now.

Also auto feels much easier for people to use, although it's semantically the same as scroll you suggested previously

1 Note that we no longer have a separate anchor-scroll element due to #8675

xiaochengh commented 1 year ago

I'm wondering if the default value should be auto or none. Maybe none is better.

It's pretty common to have a scroller with overflow: auto, which doesn't show a clear clip boundary to users when the content doesn't overflow. However, it should still apply clipping for anchor-clip: auto, which can be quite confusing to users, and developers may actuall want the positioned element (e.g. a tooltip) to overflow such a scroller.

On the other hand, it can be less confusing when the default value is none, so that developers can opt-in when they need clipping.

kizu commented 1 year ago

I would say that in my experiments I, sometimes, wanted to have the auto, but mostly wanted the none (the current behavior).

Also, I imagine clipping would also work for overflow: hidden, and that could mean that clipping by default could lead to the content being not accessible if it would go beyond the overflow of such elements.

clshortfuse commented 1 year ago

I've coded my tooltips to be a psuedo top-layer, so they break through their anchor's boundary. For example, this is anchored to the button, but overlays above the Nav Drawer and its scrollbar on the left:

image

I can simulate a clip by dropping the z-index:

image

An example of "don't render" because the anchor is off-screen is applicable to what I call "semi-sticky" toolbars, where it scrolls aways on scroll down, but stickied on scroll up. If the keyboard is focused on a toolbar button, the tooltip is shown. But when the toolbar button is no longer more than 50%, then it meets my custom threshold of being "off-screen" and I close the tooltip.

image

And mid-hide when scrolling down:

image

The consequence of not doing this is the issue we know where a tooltip can appear when it shouldn't:

image

I'm using intersection observer on the anchor as well as on the tooltip itself. Because the tooltip isn't useful without what it's giving a tip for, I want it on the anchor. But there could be an instance where the tooltip gets occluded (eg: forced position) and then I want it to hide instead of reposition when partially occluded.

I'm unsure what anchor-clip means here. Is it when the anchor (eg: the button) is clipped? Or do we mean to clip the tooltip? Is my terminology wrong? Is "anchor" the button or tooltip here?

xiaochengh commented 1 year ago

For example:

viewport
+- scroller
|  +- anchor
+- popover

Then anchor-clip: auto makes popover additionally clipped by scroller

tabatkins commented 1 year ago

Yeah, it basically gives you back the clipping that you would have if your positioned element was an abspos next to the anchor. Being able to have your positioned element elsewhere in the page is a big part of what makes anchor positioning powerful.

Also, I imagine clipping would also work for overflow: hidden

Yes, overflow:hidden is a scroller so it would act identically to overflow:scroll.

yisibl commented 1 year ago

And we don't have full support for shift yet. I have a hacky demo, but it doesn't work with position: fixed, so it probably doesn't work for popovers.

@xiaochengh Will css-anchor consider implementing a shift effect in the future?

xiaochengh commented 1 year ago

@yisibl Yes, but I don't have a clear idea how it should work. Anyway, it's better to file a separate issue for it.

css-meeting-bot commented 12 months ago

The CSS Working Group just discussed [css-anchor-1] Need ability to say "don't render" when anchor is off-screen, and agreed to the following:

The full IRC log of that discussion <emeyer> xiaochengh: In many cases, the anchor positioned element and its anchor are not in the same scroller
<emeyer> …Elements can appear hanging outside scroller, which looks weird
<emeyer> …There are use cases where you want that, but others where we want the element to act as if it’s in the scroller, so clipped
<emeyer> …Since both behaviors have use cases, we should add a new property tentatively named `anchor-clip` with values `none` and `auto` which means we want to clip using all ancestor scrollers of the anchor
<emeyer> …Not sure which should be default, but consensus is currently that it should be `none`
<emeyer> TabAtkins: While the exact mechanics of `auto` need work, you should be able to get the same clipping behavior you’d get if the anchored element is sitting right next to the anchor in the DOM
<emeyer> fantasai: Can someone drop the grammar being proposed into IRC?
<xiaochengh> anchor-clip: auto | none
<xiaochengh> default value: none
<emeyer> fantasai: I think this makes sense to have
<kizu> q+
<vmpstr> q+
<emeyer> …The one thing I’m a little unclear on is, you’re not clipping the anchor, so maybe a different name would be better
<astearns> ack kizu
<emeyer> TabAtkins: We have a l ot of anchor-* properties not actually affecting the anchor
<emeyer> …Not opposed to changing the name
<emeyer> bramus: We are either not clipping and element or else completely clipping by its bounds
<bramus> s/bramus/kizu
<emeyer> …This might have a connection with fallbacks
<astearns> ack vmpstr
<emeyer> …I wanted to mention that you might want to clip depending on the fallback, or clip as a final fallback strategy
<emeyer> vmpstr: Are there any other properties where, this is a situation where an element determines whether it should be clipped by things it’s not related to in the hierarchy
<emeyer> …If that’s the choice, how does it interact with other containers like paint containment or overflow-clip?
<emeyer> TabAtkins: The exact mechanics are still up in the air
<emeyer> …The idea is that it should act like it’s local, so I suspect you’d want things like paint clips to clip as well
<emeyer> xiaochengh: I’m not 100% sure but I did a rough prototype and the clips apply on each other
<emeyer> …We’re just doing an intersection of all the clips
<emeyer> TabAtkins: I’m no longer certain paint clip should apply, so we definitely need to discuss more
<emeyer> astearns: Should we discuss and come back to this at the next meeting?
<emeyer> TabAtkins: Can we resolve on basic behavior with details up in the air?
<emeyer> fantasai: I think so
<emeyer> astearns: Proposed resolution is we add something to deal with clipping
<emeyer> RESOLVED: we add something to deal with clipping in this context
<fantasai> s/context/context, continue figuring out details in the issue
khushalsagar commented 12 months ago

How do non-trivial clips like clip-path/border-radius behave? Implementation wise it seems like unless its simple rects (which can be collapsed) it'll be very difficult.

vmpstr commented 12 months ago

It also wasn't clear whether the desire is to have the anchored element be clipped by the (immediate or all?) scroller(s) or whether any ancestor clip should play a part, including things like overflow-clip-margin

fantasai commented 7 months ago

Tab was just describing this proposal to me, and I think... I can't see any use case for wanting to anchor to an object that's been scrolled completely outside its scroll container.

What you want is to be able to use the intersection of that scroll container and the anchor element inside it as the anchor box, and to hide the anchorpos when the anchor box clips down to zero.

tabatkins commented 7 months ago

Okay so while it's good that we got a resolution to do something in this space, we still need to work out details.

As far as I can see, there are two independent cases we might want to address:

  1. Clip as if you're sitting next to your anchor. That is, use precisely the clipping that your anchor does, so there's no difference in clip behavior between "popup is next to the anchor in the markup" and "popup is somewhere unrelated in the markup", or between "popup is using abspos" and "popup is using fixpos/top layer".
  2. Hide yourself when your anchor is clipped away. For example, a sidenote displaying alongside a scrollable region of text can't reuse the clip area like in (1), since it's positioned outside the scrollport bounds already, but it still wants to stop rendering when the anchor is scrolled out of the scrollport. Possibly we want to differentiate between "anchor starts to get clipped by something" and "anchor is fully clipped away". Probably we want to use the anchor's border box; I doubt this needs to be controllable.

(2) might be most simply solved by allowing a positioned element to be clipped by its inset-modified containing block? The sidenote case, for example, would look like:

.sidenote {
  position: fixed;
  top: anchor(--container top);
  bottom: anchor(--container bottom);
  left: anchor(--container right);
  right: 0;
  anchor-default: --target;
  align-self: anchor-center;
  clip-to-containing-block: true;
}

This does rely on anchor-center for positioning, tho; if you wanted any other alignment you'd be out of luck, since you're having to use your top/bottom to give yourself clipping bounds. It also relies on the scroll container having an anchor-name, rather than just relying on the nearest scroll container of the default anchor, which is probably what you want. So probably we do instead want to more explicitly reference the bounds of some scroller, defaulting to the default anchor element's nearest scroll container, like:

.sidenote {
  position: fixed;
  anchor-default: --target;
  top: anchor(top);
  left: anchor(--container right);

  /* vertically clips to the bounds of the default anchor's nearest scroller */
  position-clip: vertical;

  /* inline-axis clips to the specified element, which must be a scroller */
  position-clip: inline --some-container;
}

Hmm. More thought is needed here.

fantasai commented 7 months ago

For the first case, we might be able to solve by simply saying that when your containing block is the scroller (or inside it) then you get clipped just like the contents of that scroller. See #9868. Then we can just have the second case be the behavior in all other cases.

fantasai commented 6 months ago

Tab and I discussed the uses cases in this issue and #9380 and tried to come up with a proposal to handle them all. There are a few major cases to address:

  1. Make a positioned element act like it's inside its anchor's scroller (clipped by the intervening scrollers). This is already proposed to be handled by position-container. See earlier comment and issue 9868.
  2. Make the positioned element act like it's outside the anchor's scroller (as normal), but hide itself if the anchor would be clipped away by the intervening scrollers. (Doesn't make sense to display the positioned element if its anchor isn't visible.) See Tab's comment.
  3. Make a positioned element hide itself if all of its position-try-options result in overflow. See issue 9380.

Our current suggestion for handling both 2 and 3 is a new position-visibility property:

position-visibility: always | [ anchors-valid | anchors-visible ] || no-overflow

In all these cases, “hide” means that it (and all of its contents) are invisible (like ''visibility: hidden'') and do not contribute to scrollable overflow.

vmpstr commented 6 months ago
  • where invisible means either its not visibility: visible or it's been clipped by overflow of an intervening container. (We're also considering clip-path for this check.)

Can you clarify what is "it" here, is it the anchor element's border box? Or does it include ink overflow? Or is it something else?

We're discussing a similar issue with view transitions in #8282 in trying to define what "offscreen" means for vt elements, so having a common definition for when we consider something not user-visible-enough would be great.

tabatkins commented 6 months ago

Currently the definition is up in the air. I agree we should be consistent with VT (and scroll animations?).

kizu commented 6 months ago

In all these cases, “hide” means that it (and all of its contents) are invisible (like ''visibility: hidden'') and do not contribute to scrollable overflow.

One thing that we will need to handle: make the position-visibility transition in the same way as visibility itself, from Web Animations spec:

For the visibility property, visible is interpolated as a discrete step where values of p between 0 and 1 map to visible and other values of p map to the closer endpoint; if neither value is visible then discrete animation is used.


A related note: if the position-visibility will not work in the same as visibility in a “can be overridden on a child” way, this new property could be used as a hack for “non-overridable” visibility (given the element is abspos). If this will be the case, we could want to think about introducing a new keyword to visibility itself that will make it possible to achieve the same effect (making it non-overridable on the children). I wrote an article on visibility at some point, and I remember encountering use cases for this behavior (but that is probably better to open as a separate issue if there is no such yet).


All of that said, would it be possible to somehow make it apply the visibility itself? It will make things a lot simpler from the author standpoint.

chrishtr commented 6 months ago

In all these cases, “hide” means that it (and all of its contents) are invisible (like ''visibility: hidden'') and do not contribute to scrollable overflow.

I think we should amend it to remove the part about not contributing to scrollable overflow. Here's why:

I'm good with the position-visibility proposal @fantasai wrote with this small change, and think it is easily implementable.

khushalsagar commented 6 months ago

Making it no longer contribute to scrollable overflow would require layout to happen in response to scroll

Isn't that already the case because scrolling can change the position of the anchor such that a different fallback needs to be used for the anchored element? Since scrolling is threaded I figured the re-layout for such cases happens whenever the main thread receives the scroll offset, so possibly not perfectly in sync with scrolling.

chrishtr commented 6 months ago

Isn't that already the case because scrolling can change the position of the anchor such that a different fallback needs to be used for the anchored element? Since scrolling is threaded I figured the re-layout for such cases happens whenever the main thread receives the scroll offset, so possibly not perfectly in sync with scrolling.

The adjustment of the position of an anchored element due to scroll doesn't affect layout overflow (or shouldn't--maybe the anchor-positioning spec doesn't spell this out yet). This is similar to how sticky position offsets once the element becomes "stuck" also don't affect it.

tabatkins commented 6 months ago

The adjustment is currently specified as an additional translate, so without further specification it would have the same behavior as translations, which do affect overflow. But we can change that.

css-meeting-bot commented 6 months ago

The CSS Working Group just discussed [css-anchor-1] Need ability to say "don't render" when anchor is off-screen, and agreed to the following:

The full IRC log of that discussion <fantasai> -> https://github.com/w3c/csswg-drafts/issues/7758#issuecomment-1965540529
<dbaron> fantasai: we discussed use cases about not rendering when stuff doesn't fit
<dbaron> fantasai: 3 major use cases (repeating the comment in the issue)
<dbaron> fantasai: (describes bullets 1-3 from https://github.com/w3c/csswg-drafts/issues/7758#issuecomment-1965540529)
<fantasai> position-visibility: always | [ anchors-valid | anchors-visible ] || no-overflow
<dbaron> fantasai: (describes position-visibility suggestion from https://github.com/w3c/csswg-drafts/issues/7758#issuecomment-1965540529 )
<una> q+
<astearns> ack una
<dbaron> fantasai: (finishes describing proposal)
<emilio> q+
<emeyer> q+
<dbaron> una: I think this is really important for a lot of anchor psoition use cases. I don't want to block this but I wonder if position-visibility is the best name. Sounds like describing different behavior for position... maybe call something like position-behavior. When you said it it was clear, but looking at the names/keywords it wasn't as clear to me.
<dbaron> una: I don't want to bikeshed this forever but wondering if there are better names.
<khush> q+
<fantasai> scribe+
<astearns> ack dbaron
<fantasai> dbaron: When you said "like visibility: hidden", wondering if that was correct, or if stronger
<fantasai> ... it normally contributes to overflow, can be overridden by descendants
<fantasai> TabAtkins: not "display: none" ish, because that modifies box tree
<fantasai> TabAtkins: stronger in that children can't turn off
<fantasai> TabAtkins: chrishtr suggests it does contribute to overflow, just turns off paint
<dbaron> TabAtkins: not display:none-ish because that changes the box tree. Shouldn't be stronger in that children can't turn it off (?). But chrishtr asking below for it to not suppress overflow. The argument seems reasonable to me. So I'm going to suggest we remove that part of the proposal and just suggest it turns off paint.
<dbaron> fantasai: I don't understand reasoning for that.
<dbaron> TabAtkins: The thing that it's overflow and the thing that it's setting its containing block might not have relation to each other.
<dbaron> TabAtkins: so being able to avoid setting contributing to overflow doesn't help too much anyway.
<emeyer> q-
<dbaron> TabAtkins: There are several ways to raise the positioned element further up the box tree for these purposes so it won't care about sub containers anyway. e.g., a fix pos or top layer, or move higher in the dom. Can avoid scrollability issues that way.
<dbaron> There are ways around if there is a scroll problem. Even when you are not moving it around... in a place where it's potentially subject to it, not a strong connection there.
<dbaron> fantasai: I think wrong about not impacting scrollable overflow.
<dbaron> astearns: The conversation TabAtkins mentioned with chrishtr was about not having the no-overflow value?
<futhark> q+
<dbaron> TabAtkins: not about changing the values, just removing the rule about not contributing to scrollable overflow when it's hidden.
<astearns> ack emilio
<astearns> q+
<dbaron> emilio: The "like visibility" thing made me think ... making it just suppress paint seems suspicious to me, you can still have things like focused elements inside. visibility:hidden does prevent focusability, but I don't think we want determining focusability to need layout. Has this been considered?
<dbaron> fantasai: That's why it should be like visibility, not just paint suppression: we want it to block hit testing, focusability, rendering into speech. And we don't want children to override it.
<dbaron> emilio: do you really want it not to be accessible?
<dbaron> fantasai: Yes. If it's not visible to sighted user, it shouldn't be accessible
<dbaron> emilio: it's accessible depending on the scroll position -- you're supposed to be able to scroll down and see it
<dbaron> TabAtkins: so why woud ???
<dbaron> emilio: also about search, find-in-page, etc.
<dbaron> emilio: a11y client may want to see contents of popup even if it's not visible
<dbaron> fantasai: If it scrolls off the screen that's fine, you can still access it. This is for cases where you want to hide it when it scrolls off.
<dbaron> fantasai: these are cases where you don't want the popup to be visibile based on where it lays out
<dbaron> TabAtkins: If you find-in-page... it still doesn't fit, it won't be displayed.
<dbaron> TabAtkins: not sure how we'd want to present the find-in-page result.
<dbaron> TabAtkins: It's possible moving things around might reveal it, but there's no straightforward way to determine how to do so. Depends on how fallbacks are set up. So I don't think there's an automatable way to expose the text inside to the user.
<dbaron> emilio: It would be exposed if it was clipped by... we don't have other precedent for scroll position affecting focusability. You can totally find-in-page and focus clipped and overflow:hidden text.
<dbaron> TabAtkins: You can scroll that in
<dbaron> emilio: you're never going to see it.
<khush> q?
<dbaron> emilio: you have the same problem... but you still have the same problem.
<dbaron> emilio: a11y hacks rely on pushing stuff away from the viewport.
<dbaron> emilio: I think that and making element.focus() require layout are concerning. element.focus() is hot. Every engine goes through hooks to try to avoid work there.
<dbaron> chrishtr: why does it cause more layout?
<dbaron> emilio: You may be focusable if you're visible but not focusable if you're not.
<dbaron> emilio: you can't determine focusability only with style any more -- you need layout
<dbaron> chrishtr: if it's hidden but focusable then a sighted user could a thing they can't see is focused
<dbaron> emilio: That's my question -- a lot of things to do here. Just act like visibility. Maybe remove from a11y tree based on layout not a big deal?
<jcraig> q+ to ask if focus would scroll to visible and then cause render?
<dbaron> TabAtkins: Container queries already put us in a situation where layout can affect a11y tree.
<dbaron> emilio: but they affect style
<dbaron> emilio: so different from an implementation perspective
<dbaron> TabAtkins: ...
<dbaron> emilio: engines try to avoid reevaluating ??? when CQ are in use.
<dbaron> emilio: I don't know at which stage you'd bring stuff back to live for a11y tree.
<dbaron> emilio: It's slightly different implementation-wise. You want it to keep generating boxes.
<jcraig> q+ to ask or would search cause scroll-to-visible and then render
<dbaron> chrishtr: if you scroll so anchored element has to disappear because of offscreen, then you go back. And anchored element was focused before visible. You'd want it to retain focus when back on screen. That's what user would expect.
<dbaron> emilio: not what would happen when working with visibility
<vmpstr> this is what content-visibility: auto would work like fwiw
<dbaron> chrishtr: what if there was script that applied inert and removed it... does the focus come back if it's lost?
<dbaron> chrishtr: I don't think focus remembers?
<dbaron> emilio: There's a focus fixup in the spec. It checks whether the current active element is still focusable and blurs it if not... but doesn't remember the previous one.
<astearns> ack jcraig
<Zakim> jcraig, you wanted to ask if focus would scroll to visible and then cause render? and to ask or would search cause scroll-to-visible and then render
<dbaron> jcraig: Maybe a naive question... but curious if the view is in a state where if the user would tab to it, it would be a state where it would be focused and hidden. Would the focus cause it to scroll into visible and then render? Would in-page search have similar effect, would the search cause it to scroll into visible and then render? If so I wouldn't consider it a problem... but if scrolling wouldn't cause it to render that would be a
<dbaron> problem.
<dbaron> TabAtkins: 2 of the values not affected by scroll position or not. The third requires the anchor to be scrolled, which isn't the same as scrolling the popup into view and might not be possible anyway.
<khush> q-
<dbaron> jcraig: if there's no scenario where a sighted user would expect focus to changge the visibility, then you'd probably want them hidden by AT by default as well.
<fantasai> scribe+
<astearns> ack dbaron
<Zakim> dbaron, you wanted to react to emilio
<fantasai> dbaron: I wanted to say something close to what jcraig said
<fantasai> dbaron: my understanding is that you want AT to show similar to what's shown in other modes
<fantasai> ... because you don't know what combination of things the user is using
<fantasai> dbaron: consider they might be using a mix of things, and want to be consistent
<vmpstr> q+ to mention c-v
<chrishtr> q+
<astearns> ack futhark
<astearns> q-
<fantasai> futhark: Wrt overflow contributing to overflow, wouldn't you get circularity?
<fantasai> futhark: if you remove scrollbars then it changes the anchor's position, gets more space, becomes visible, -> cycle
<kizu> q+
<dbaron> futhark: I can a comment on a separate part -- the overflow contributing to overflow. If it stops contributing to overflow, won't you get circularity with scrollbars? Or does that go into normall scrollbar handling?
<fantasai> TabAtkins: that is a possibility of circularity, but possibly solveable same way as tall element with aspect ratio
<dbaron> TabAtkins: That is a possible circularity, but maybe solvable like a tall element with an aspect ratio.
<dbaron> chrishtr: is that the same issue I mentioned in issue?
<dbaron> chrishtr: my suggestion was tooltip always contributing to scrollable overflow so you don't have layout circularity
<astearns> ack vmpstr
<Zakim> vmpstr, you wanted to mention c-v
<dbaron> vmpstr: I wanted to mention content-visibility as one of the things that has semantics of not painting when offscreen but is still available to AT. Focus and find-in-page work in similar ways -- you do the rendering just-in-time to make sure the content is available. I fully realize here you're scrolling a different thing, but that's the only difference I see.
<astearns> ack chrishtr
<dbaron> vmpstr: So I think there's a precedent for hiding this that are still available to assistive technologies.
<flackr> q+
<astearns> ack kizu
<dbaron> kizu: I think both for an element impacting overflow and for a hidden element being able to be searchable/accessible -- both things we could want to control not just for popovers/anchors but for anything. Might want element to be there but not contribute to overflow (decorative). The same for searchable: you want to search something that's hidden by display:none/visibility:hidden/etc., want it to be present in search.
<Penny> I need to drop
<jcraig> q+ to respond to vmpstr comment re: content-visibility "not painting when offscreen but is still available to AT."
<dbaron> kizu: I agree if it behaves like visibility:hidden should be hidden -- this ability to hide is for some optional thing that doesn't necessarily need to be displayed. For most use cases removing focus and not returning it back seems ok.
<dbaron> kizu: Can thingk of 2 ??: one is scroll driven animations.
<dbaron> kizu: I think it's possible to detect anchor outside of current overflow. ??
<dbaron> kizu: If you have a cursor and then scroll, cursor could affect elements based on :hover pseudo class.
<dbaron> kizu: I think it's ok for tooltips to disappear and lose everything inside because you don't see them anymore.
<astearns> ack fantasai
<Zakim> fantasai, you wanted to comment on why we need not contributing to overflow
<chrishtr> q+
<dbaron> fantasai: We don't these to contribute to scrollable overflow when they're hidden because it's an optional tooltip-ish thing that's nice to have if there's room. If it's overflowing the scrollport you don't want to introduce scrollbars to accomodate an invisible thing the user can't see.
<masonf> Don't those scrollbars indicate to the user that there might be something down there to scroll to?
<dbaron> fantasai: That's not a good user experience. The point of hiding this is that when the condition occurs, the item isn't relevant enough to display it.
<masonf> (Like the hidden popover?)
<dbaron> fantasai: If it's not display it should behave like it's not there.
<dbaron> fantasai: We didn't want to make it display:none because that has side effects we don't want.
<dbaron> fantasai: But we want the page to behave as if it wasn't there for visible rendering and for layout. suprress rendereing, events, being in a11y tree. That includes not contributing tos crollable overflow.
<astearns> ack flackr
<dbaron> flackr: I heard a bunch about scrolling the anchor into view: I think we might want to spec that focusing something in the anchored thing, we might want to scroll the anchor into view. Should think whether that makes sense in all conttexs.
<TabAtkins> If we did "scroll the anchor into view", we'd have to key it off the default anchor.
<dbaron> flackr: For contributing to scrollable overflow: there may be use cases on theo pposite side. May be some cases where you want to have room to show. I thought we have another CSS property for whether thigns contribute to scrollable overflow.
<chrishtr> https://github.com/w3c/csswg-drafts/issues/8361 discusses adding a CSS property for avoiding scrollable overflow
<dbaron> TabAtkins: Don't have it yet, have an open issue for it.
<dbaron> flackr: yeah, meant we have an open issue.
<dbaron> flackr: If we resolved on that issue we'd have a way for authors to choose one method or the other.
<emilio> q+
<dbaron> astearns: in the proposal it mentions the new overflow value would apply even when using abs pos or fixed pos. Maybe that's an indication it belongs in the more general property.
<astearns> ack jcraig
<Zakim> jcraig, you wanted to respond to vmpstr comment re: content-visibility "not painting when offscreen but is still available to AT."
<dbaron> jcraig: Responding to earlier comment about precedent for having hidden contents interactable with AT. Typically what's available when something's off screen is the ability to find it and bring it on screen when the user interacts with it. If you have negatively positioned content, e.g., way off to the left, that isn't something we can ever scroll to. If you use that for the old "skip to main content" link. Then there's a time when...
<astearns> ack chrishtr
<dbaron> jcraig: AT is interacting with something offscreen. Exposure to AT often means ability to search/find and bring into view. So I think there's a distinction.
<dbaron> chrishtr: I think I'm convinced by kizu's comments about developers intentionally making things hidden. I think anchored element is important to the user in relation to other thing, that's no longer on the screen. If tha were scrolled back onto screen, then it would be on the screen and things would be consistent. So I think I agree it makes sense to hide it for all of them, or bring it back for all of them.
<dbaron> chrishtr: Re scrollable overflow: the anchored element contributes to scrollable overflow whether hidden or not hidden. So the issue about scrollabrs that might not make sense would potentially apply in either case.
<dbaron> chrishtr: as TabAtkins mentioned there are ways to avoid that problem and meet needs of anchor positioning.
<dbaron> chrishtr: So I think that's not a necassiry feature for useful anchor positioning on the web
<khush> q+
<kizu> q+
<dbaron> chrishtr: I think worth considering another CSS property about whether element contributes to overflow.
<dbaron> chrishtr: another use case is transformed elements
<astearns> ack emilio
<fantasai> astearns, we'd like to get a resolution to republish before closing the meeting, even if these issues stay open
<dbaron> emilio: I wanted to ask flackr if other use cases for contributing to scrollable overflow or not -- but afaict the separate property wouldn't allow to not contribute only when hidden.
<astearns> fantasai: ack
<dbaron> flackr: I was saying if you set the property to not contribute to overflow, then you'd always be in the case where there wasn't sufficient space to match the position-try blocks
<kizu> q-
<dbaron> emilio: not sure i follow, but maybe not important
<dbaron> emilio: other thing: not sure if we have a similar issue for ... gecko has a mechanism to hide something visually but not from AT. I guess that could also be useful here.
<astearns> ack khush
<jcraig> s/find and bring into view. So I think there's a distinction./find and bring into view, at which time its rendered and the AT user can interact with it. So I think there's a distinction between "available for AT to bring something into view" and "available for an AT user to interact with while still offscreen/unrendered"./
<dbaron> khush: Did I understand chrishtr correctly: if anchor is offscreen which causes anchor to be hidden -- you can't use find-in-page to search fortext in anchorede elemnt?
<flackr> To be more specific, I was arguing that I don't think authors need to conditionally contribute to overflow, either always contribute or never (and sometimes not be possible to show the anchored element).
<dbaron> chrishtr: yes... reasoning is that the anchored element is important only in relation to the anchor. If developer specifies hidden thing then developer says it's not relevant when offscreen.
<dbaron> chrishtr: default value of this property would still be visible always
<dbaron> khush: Definitely cases where want to search for text in anchored element... but seems find given default.
<dbaron> chrishtr: polyfills sometimes put visibility:hidden which has same effect as what we're discussing
<dbaron> astearns: A few ways forward. I think we mostly agree this is useful and needed for some use cases.
<dbaron> astearns: I think we could continue discussing in issue. Could resolve on putting proposal in spec or opening separate issue in the spec. Or could resolve on adding a subset that we agree on into the spec.
<dbaron> fantasai: my impression is that there's a lot of confusion about what this is and what it ought to do.
<dbaron> fantasai: I think TabAtkins and I need to clarify that or explain it better or work with bramus on demos or something.
<dbaron> fantasai: I don't feel strongly about putting in draft or not.
<dbaron> fantasai: but I would like to publish the current draft as is.
<dbaron> fantasai: so maybe we add this into the editor's draft after we publish
<dbaron> TabAtkins: I didn't see much confusion -- but also don't see how we can subset it.
<dbaron> TabAtkins: so my preference is either to continue discussing, or put it in the spec with inline issues.
<dbaron> astearns: I like the idea of publishing as-is and adding to ED right after
<dbaron> chrishtr: should we resolve on what should be in ED spec? everything didden?
<dbaron> s/didden/hidden/
<dbaron> astearns: guessing having a particular position in the ED may make it easier to discuss alternatives. Having a stake in the ground in the ED can be a good way forward.
<dbaron> astearns: so what exactly would we add to the ED?
<dbaron> fantasai: My suggestion is adding what TabAtkins and I originally proposed, and then mark the open questions against it.
<dbaron> chrishtr: would you mind removing the scrollable overflow part, or prefer not to?
<dbaron> fantasai: I'd prefer not to... but happy to mark that as an issue. Issue largely around circularity.
<dbaron> Proposed resolution: Add position-visibility as proposed in the issue with concerns noted as issues in the draft, to the editors draft *after* publication of working draft.
<dbaron> RESOLVED: Add position-visibility as proposed in the issue with concerns noted as issues in the draft, to the editors draft *after* publication of working draft.
<khush> One more sub-issue that can be done in a follow up, define what "offscreen" means for the anchor.
khushalsagar commented 6 months ago

@fantasai @tabatkins should I file a separate issue to clarify how we decide whether the anchor is offscreen? This comment suggested taking an intersection of the scroll container with the anchor element but its unclear what the reference box is (border box or ink overflow rect). And also why use the nearest scroll container instead of the viewport, what happens if the scroll container itself is offscreen. Would love to get a common definition of whether an element is onscreen since VT needs the same concept.

chrishtr commented 6 months ago

how we decide whether the anchor is offscreen?

I think we should use the same definition as IntersectionObserver viewport visibility. That is simple, consistent, makes it easy for implementations to use that to hide or show the anchored element, and at least one developer mentioned on this issue they already use this definition in their polyfill.

khushalsagar commented 6 months ago

IntersectionObserver has a mode to check occlusion. I'm assuming we're not including that here?

chrishtr commented 6 months ago

IntersectionObserver has a mode to check occlusion. I'm assuming we're not including that here?

Correct, we would not use that mode. We'd just use the one that applies clips and filters, not the "v2" one.

wangxianzhu commented 6 months ago

We'd just use the one that applies clips and filters

Currently IntersectionObserver v1 doesn't respect filters, see https://github.com/w3c/IntersectionObserver/issues/509 and https://crbug.com/40937025. However, internal intersection observers respect filters to follow the corresponding specs (e.g. contents-visibility). We may need to either resolve this discrepancy in intersection observer spec or specify we need to consider filters here in css-anchor-1.

khushalsagar commented 6 months ago

We'd just use the one that applies clips and filters

The algorithm [here](https://w3c.github.io/IntersectionObserver/v2/#calculate-intersection-rect-algo:~:text=Let%20intersectionRect%20be%20the%20result%20of%20running%20the%20getBoundingClientRect()%20algorithm%20on%20the%20target.) uses the result of getBoundingClientRect() as the element's box to intersect against. IIUC that's based on border box so won't include overflow from filters like blur. If you want to include filters, wouldn't using the element's ink overflow rect be better?

chrishtr commented 6 months ago

The algorithm [here](https://w3c.github.io/IntersectionObserver/v2/#calculate-intersection-rect-algo:~:text=Let%20intersectionRect%20be%20the%20result%20of%20running%20the%20getBoundingClientRect()%20algorithm%20on%20the%20target.) uses the result of getBoundingClientRect() as the element's box to intersect against. IIUC that's based on border box so won't include overflow from filters like blur. If you want to include filters, wouldn't using the element's ink overflow rect be better?

I suggest we take this particular point to the Intersection Observer spec.

tabatkins commented 6 months ago

And also why use the nearest scroll container instead of the viewport, what happens if the scroll container itself is offscreen. Would love to get a common definition of whether an element is onscreen since VT needs the same concept.

What we want is that the element is clipped away "entirely", by some combination of overflow clip rects (and maybe other clipping operations?). This should include all the scroll containers up to the nearest inclusive ancestor of the positioned element's own containing block, at least; higher than that and you're already guaranteed that both elements are getting clipped by the same scroll container, so there's no need to specially care about them.

As for what precisely to consider "the box" that is getting clipped, I agree we should defer to what IO/etc are doing for consistency.

wangxianzhu commented 5 months ago

I suggest we take this particular point to the Intersection Observer spec.

I think we still need to address the filter issue here. See #issuecomment-1994974242.

Another discrepancy from the IntersectionObserver spec is about visibility of the anchor element. IntersectionObserver doesn't consider visibility. This is mainly a note for implementation not to merely rely on IntersectionObserver for posiiton-visibility: anchor-visible.

wangxianzhu commented 5 months ago

One thing that we will need to handle: make the position-visibility transition in the same way as visibility itself, from Web Animations spec:

Do we need to handle the transition of actual visibility (not the value of position-visibility property)? This seems only feasible if the spec defines a new visibility value and lets the UA update the value of visibility when the visibility requirement of the anchored element changes.

Or can we just use the existing values of visibility (i.e. allow descendants to override with visibility: visible)? The author will have the flexibility to just hide part of the anchored subtree. It may look weird, but it could be a choice of the author.

Edit: defining the visibility property in spec may limit the ability of implementation. I think it's reasonable to require non-overridable invisible status of the subtree for now. In the future, we could create new APIs to define the styles of visible/invisible states for fancier effects, e.g. fading in/out with opacity transition.

wangxianzhu commented 5 months ago
  • no-overflow hides the abspos if it overflows its inset-modified containing block (after exhausting all position-try options, if relevant). Note this value would apply even when using regular abspos or fixedpos.

Can you clarify which of the following of the abspos should be used to check if it overflows its inset-modified containing block?

  1. the padding box;
  2. the unclipped scrollable overflow;
  3. the ink overflow.

Edit: Now I understand this depends on the Overflow Management chapter. Chromium's implementation of position-try checks overflow using the margin box of abspos.

wangxianzhu commented 5 months ago

During the implementation of anchors-valid and anchors-visible, I found the following things may need clarification about validity/visibility of anchors:

  1. Is an anchor valid or visible if
    • it has display: none,
    • it's under content-visibility: hidden, or
    • it's under content-visibility: auto and is currently not rendered.

Our current plan is to treat them as invalid because the anchor doesn't exist in the layout tree.

  1. For validity of anchor references,
    • should we iterate all CSS properties to find invalid anchor references? For example, is border-width: anchor(--a top) an invalid anchor reference or should it be ignored?
    • should we check the validity of anchor references from unused @position-try styles?
    • should an anchor reference with missing default anchor (e.g. width: anchor-size(width) without a default anchor) be treated as invalid or be ignored?
    • should we check the validity of anchor references from anchor-size()?

Our current plan is to only check for validity and visibility for anchor references that are actually resolved, but not including those in unused @position-trys.