w3ctag / design-reviews

W3C specs and API reviews
Creative Commons Zero v1.0 Universal
319 stars 55 forks source link

Specification review for CSS Anchor Positioning #848

Open xiaochengh opened 1 year ago

xiaochengh commented 1 year ago

こんにちは TAG-さん!

I'm requesting a TAG review of CSS Anchor Positioning.

CSS Anchor positioning is a CSS feature that allows an element to position and size itself relative to one or more "anchor elements" elsewhere on the page. A typical use case is to "pin" a tooltip to something that triggers it.

Security and Privacy self-review:

2.1. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary?
No additional information.
2.2. Do features in your specification expose the minimum amount of information necessary to enable their intended uses?
Yes.
2.3. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them?
No PII.
2.4. How do the features in your specification deal with sensitive information?
No change.
2.5. Do the features in your specification introduce new state for an origin that persists across browsing sessions?
No.
2.6. Do the features in your specification expose information about the underlying platform to origins?
No.
2.7. Does this specification allow an origin to send data to the underlying platform?
No.
2.8. Do features in this specification enable access to device sensors?
No.
2.9. Do features in this specification enable new script execution/loading mechanisms?
No.
2.10. Do features in this specification allow an origin to access other devices?
No.
2.11. Do features in this specification allow an origin some measure of control over a user agent’s native UI?
No.
2.12. What temporary identifiers do the features in this specification create or expose to the web?
None.
2.13. How does this specification distinguish between behavior in first-party and third-party contexts?
No change from existing behavior.
2.14. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode?
No change from existing behavior.
2.15. Does this specification have both "Security Considerations" and "Privacy Considerations" sections?
Yes.
2.16. Do features in your specification enable origins to downgrade default security protections?
No.
2.17. How does your feature handle non-"fully active" documents?
No change from existing behavior.
2.18. What should this questionnaire have asked?
Not expecting any.

Further details:

We'd prefer the TAG provide feedback as (please delete all but the desired option):

💬 leave review feedback as a comment in this issue and @-notify @xiaochengh

LeaVerou commented 1 year ago

We looked at this today. Overall this looks great, and covers use cases that developers desperately need covered.

Q: What is the fallback if no @anchor-fallback is specified? Is there no fallback, or is the fallback UA dependent?

tabatkins commented 1 year ago

There is no fallback, it just stays where you told it to stay. There's plenty of reasons to not do a fallback, and no reasonable way to predict what fallback would work in an automatic fashion anyway.

LeaVerou commented 1 year ago

Don’t you have the same problem with explicit fallbacks as well, if none of the specified fallbacks work? What do you get in that case?

There's plenty of reasons to not do a fallback,

What are they?

and no reasonable way to predict what fallback would work in an automatic fashion anyway.

The spec already proposes a simple mechanism for this, in a note. Even that is better than nothing.

In the spirit of "simple things should be easy, complex things should be possible", I think it would be good to have some kind of automatic fallback for the very very common case where authors want to guarantee that the positioned element is always visible (unless that's not physically possible, e.g. it's actually larger than the viewport) and don't care about the specifics much (or care but only to some degree, i.e. this should be combinable with specifying fallbacks explicitly).

It may even make sense to make this the default (with an opt-out), but that depends on the use cases you collected (and what the reasons are that you mention), as well as the quality of the automatic fallback algorithm. From the outside, it looks like the vast majority of use cases I see would prioritize being always visible vs being displayed in the way specified.

The current API for fallbacks is beautiful for enabling a multitude of possibilities, but it makes the common case fairly involved, and I worry it will result in a lot of popups and tooltips that are out of the viewport a lot of the time, harming user experience for end-users.

tabatkins commented 1 year ago

Don’t you have the same problem with explicit fallbacks as well, if none of the specified fallbacks work? What do you get in that case?

I'm not sure what problem you're referring to. The behavior for fallbacks is well-specified. (Currently it's "just use the final fallback", but there's an open issue to change it to "continue using the last one that worked"; I just couldn't get to it before my vacation.)

What are they?

When the position you specified is where you want it to be. Not everything wants to move around to stay visible. This is the case for almost every element on the page normally, and there's no particular reason to assume it would be different here.

Even that is better than nothing.

No, it's much worse than nothing if the author's intent was that it's in the exact place it's supposed to be already. Moving it would be confusing and possibly mess up their design. We simply cannot infer what the author's intent was here.

The author has easy ways to express some common forms of "keep it on-screen if possible" via the auto side keyword already. There's discussion on a "sliding" behavior as well that'll probably materialize in the spec as we work out the details and be similarly trivial to apply.

(If anything, the fact that there are trivial ways to activate simple fallback without having to use @position-fallback means that if you don't use any of the fallback methods, that's a (very weak) signal that you actively don't want fallback at all.)

LeaVerou commented 11 months ago

I'm not sure what problem you're referring to.

The problem of having popups that are not visible, and are cut off or even cause scrolling. Currently the API is designed as if repositioning tooltips so they’re still visible is the exception, not the rule, yet most use cases require repositioning.

When the position you specified is where you want it to be. Not everything wants to move around to stay visible. This is the case for almost every element on the page normally, and there's no particular reason to assume it would be different here.

See, this is exactly why we ask for an explainer which includes user needs and use cases. It’s hard to review something if we're not on the same page about what problem it's solving. I was thinking the primary use cases are things like popups, popovers, menus etc. But maybe that's not the case?

No, it's much worse than nothing if the author's intent was that it's in the exact place it's supposed to be already. Moving it would be confusing and possibly mess up their design. We simply cannot infer what the author's intent was here.

Authors often forget things, or don't debug their design thoroughly. I can easily see authors testing their website on a large viewport on desktop and not considering fallbacks because everything works fine. I’m not saying authors should not be able to express their intent that their popup should not move. But if -- say -- 90% of use cases require the popup to shift position to be visible, it seems odd that it would be opt-in, rather than opt-out. So again, it all depends on what the use cases are.

The author has easy ways to express some common forms of "keep it on-screen if possible" via the auto side keyword already. There's discussion on a "sliding" behavior as well that'll probably materialize in the spec as we work out the details and be similarly trivial to apply.

It would be useful to have a more concrete example of what's the minimum code required to express "make sure this is always visible unless it literally doesn't fit".

(If anything, the fact that there are trivial ways to activate simple fallback without having to use @position-fallback means that if you don't use any of the fallback methods, that's a (very weak) signal that you actively don't want fallback at all.)

I disagree, see above.


Another question I had: Looking at the syntax, I cannot quite imagine what the code would look like for a popup that includes a pointer arrow, which is very common for these (especially if it includes position fallbacks). Is it possible, and if so, how? If it's not possible, are there any level 2 plans to facilitate these use cases?

plinss commented 11 months ago

Given the recent proposal for enhancements, we're going to pause our review until the WG has a chance to consider potential changes to the spec. Please ping us when things settle.

chrishtr commented 3 months ago

Hi, here are some updates on this review:

  1. The original author of this TAG review is no longer working on it, so I'll be posting updates and fielding questions.

  2. The spec has been updated (& republished as a WD), so I think this review should be restarted.

  3. There has been a quite substantial update to incorporate more use cases, improve developer ergonomics, add full cascade/animations support, and include a robust set of fallback options. For the latter, see section 5.1 in the spec, especially the flip keywords. @LeaVerou, I think this should satisfy the concerns you raised.

We're also working on an updated explainer/blog post about the feature.

mfreed7 commented 2 months ago

Just a quick heads-up the Chromium has put up an intent to ship for anchor positioning:

https://groups.google.com/a/chromium.org/g/blink-dev/c/jGTYNuidPRs/m/-jB4agJ7AAAJ

As noted in the I2S, we're very open to suggestions and improvements to this feature. It's rather large and surely will continue to have improvements made over time, as developers come up with new cool use cases and requirements.

LeaVerou commented 2 months ago

Hi @mfreed7, I see in the I2S TAG review is listed as "Issues addressed", but the last we said was @plinss’s comment https://github.com/w3ctag/design-reviews/issues/848#issuecomment-1648281754 :

Given the recent proposal for enhancements, we're going to pause our review until the WG has a chance to consider potential changes to the spec. Please ping us when things settle.

We have not actually reviewed this design at all. All comments were about the earlier version, which (my understanding is) is substantially different.

That said, if it's shipping, and if it would be useful, we can definitely prioritize reviewing it.

mfreed7 commented 2 months ago

Thanks for the comment @LeaVerou!

Hi @mfreed7, I see in the I2S TAG review is listed as "Issues addressed", but the last we said was…

I can see that concern. It’s more of a question about our I2S process – “Issues addressed” means we responded to questions raised so far.

We have not actually reviewed this design at all. All comments were about the earlier version, which (my understanding is) is substantially different.

Thanks. While you’re right that substantial improvements have been made in the last year, I hope you’ll find that the spirit of the feature and the overall feature set hasn’t changed much, when you get a chance to review them. Your comment from the last review was:

We looked at this today. Overall this looks great, and covers use cases that developers desperately need covered.

Q: What is the fallback if no @anchor-fallback is specified? Is there no fallback, or is the fallback UA dependent?

And then there’s a conversation further about the fallback behavior. If anything, I believe the new state of the feature, particularly the inset-area syntax, directly addresses your comments.

That said, if it's shipping, and if it would be useful, we can definitely prioritize reviewing it.

We generally find all feedback useful, and we’re committed to addressing any issues that can be addressed. So if the TAG is willing to give it a followup review, that’d be great!

LeaVerou commented 2 months ago

Hi there,

Quick question: Could you clarify the scope of anchor names? I see anchor names are defined as tree-scoped. Wouldn’t that mean that you can't have multiple elements with the same anchor name in each shadow tree or the entire light DOM? Reading the code I'd have expected that the identifier is scoped to the element it’s defined on and its descendants.

Also, the explainer in the first post seems to be of the original proposal; is there an explainer for the new proposal?

tabatkins commented 2 months ago

is there an explainer for the new proposal?

No, we did not write a new explainer for the updates. (It was already a mature proposal at this point.)

mfreed7 commented 2 months ago

Quick question: Could you clarify the scope of anchor names? I see anchor names are defined as tree-scoped. Wouldn’t that mean that you can't have multiple elements with the same anchor name in each shadow tree or the entire light DOM? Reading the code I'd have expected that the identifier is scoped to the element it’s defined on and its descendants.

So a few answers to this:

  1. Yes they are tree scoped, but that doesn't mean you can't have multiple elements with the same anchor name. The "target anchor element" algorithm finds the closest named anchor looking up the DOM tree. So you can construct repeating sections of anchor-name, position-anchor, anchor-name, etc., and each position-anchor will "find" the anchor-name just above it, which tends to be convenient in many circumstances.
  2. The anchor-scope property allows developers to get the behavior you were hoping for, limiting name scoping to descendants.
plinss commented 2 months ago

is there an explainer for the new proposal?

No, we did not write a new explainer for the updates. (It was already a mature proposal at this point.)

Note that explainers aren't just for immature proposals, and they aren't just for TAG review either. If nothing else, they'll help the next set of people who want to build on the technology by explaining why the decisions were made as they were, and what was thought about but rejected. We're trying to capture institutional knowledge.

LeaVerou commented 2 months ago

Note that explainers aren't just for immature proposals, and they aren't just for TAG review either. If nothing else, they'll help the next set of people who want to build on the technology by explaining why the decisions were made as they were, and what was thought about but rejected. We're trying to capture institutional knowledge.

Yup. Marking this as pending explainer update meanwhile.


@mfreed7 Thanks for the clarifications! We'd love some use cases for anchor-scope in the new explainer, since it's quite a divergence from how other similar things work in the Web platform.

fantasai commented 1 month ago

Fwiw, I've also had trouble getting internal reviews because the spec is so opaque, and there's no good up-to-date explainer.

However Una just posted https://developer.chrome.com/blog/anchor-positioning-api because Chrome is shipping the feature this week... So now that it's shipping and Google will push back on changes on the basis of compat constraints, you can have a good explainer to review! :upside_down_face:

mfreed7 commented 1 month ago

So now that it's shipping and Google will push back on changes

Our position remains the same, and is described in the blink intent thread:

I'd just like to reiterate that we are open to making well-justified changes to the API shape over the next few months, as concerns arise. I think that the fact that we're shipping this API makes us more (not less) likely to a) surface the need for these changes, and b) prioritize them appropriately.

@fantasai beat me to posting it here, but yes we now have a very detailed and up to date blog post about this feature:

https://developer.chrome.com/blog/anchor-positioning-api

We'd love to hear your thoughts. I just noticed that anchor-scope isn't mentioned there (anchor pos is a large feature!!) but as mentioned before, Chrome also hasn't shipped that particular property.

plinss commented 1 month ago

This looks like a generally useful feature to have, addressing a host of prominent author pain points.

Discussions on blink-dev suggest that there might not be WG consensus on the maturity of the specification. The recently-opened issues also indicate some unresolved questions.

Regarding the design of the feature, our biggest concern is that it focuses on making complex things possible, but doesn't sufficienly make simple things easy. We think the simple cases of a popup that merely shifts or flips its position be always visible should not require a separate @position-try rule or writing out the logic, authors should be able to declare intent and have things Just Work™. Otherwise, this is prone to the same bugs as hand-rolled implementations, it's just that the bugs will now live in CSS, not JS. For example, a popular library for this sort of thing is https://floating-ui.com/ (but there are many more). Notice how flipping and shifting are high level abstractions, rather than something authors have to specify in detail. If these >80% use cases are covered by simple declarations, things like @position-try could be reserved for L2, so they can be more fully baked, and informed by actual usage.

Regarding position fallbacks, it currently seems that what triggers a fallback is overflow, with no way to specify additional triggers. Is there planning for authors being able to customize the trigger in the future?

Instead of focusing customizability efforts around niche positioning use cases, we’d love to see more work towards making it possible to specify pointers, which seem so common we worry about the use case coverage of an anchor positioning feature without some way to achieve them.

We understand the rationale for using anchor-name instead of HTML ids (e.g. scoping, markup edits), but we are concerned that new properties are being added to name elements in (likely) the same way. For example, there is currently container-name, which also names an element. Is it common for these names to need different identifiers? If so, would it make sense to have a shorthand (e.g. name) of all CSS properties that name an element?

anchor-scope does not appear in the explainer(s) and does not appear to feature in the algorithm that finds an anchor.

The algorithm for finding anchors is quite complex. That is not necessarily an issue, internal complexity is often necesary to avoid external (author-facing) complexity, however the motivation needs to be explained more.

Some other notes on the current syntax:

Interactions with shadow DOM are not covered by the explainers (though they are in the specification).

Finally, we're concerned about the status of the explainer relative to the Chromium blog post. In general, we'd like to see an explainer, that starts with articulated user need, as a separate document that is written along side of the spec, and is kept up-to-date as the spec changes. The explainer thereby can become the seed for developer documentation when that's required. See https://tag.w3.org/explainers.

tabatkins commented 1 month ago

We think the simple cases of a popup that merely shifts or flips its position be always visible should not require a separate @position-try rule or writing out the logic, authors should be able to declare intent and have things Just Work™.

That is indeed already possible, by using the flip-* keywords in position-try-options. You only need to reach for the @position-try rule if you're doing non-trivial alternatives.

Regarding position fallbacks, it currently seems that what triggers a fallback is overflow, with no way to specify additional triggers. Is there planning for authors being able to customize the trigger in the future?

Yes, the syntax is open for extension to other triggers. For example, a use-case we've already identified that might want to get solved in level 2 is providing a fallback for when your anchors aren't valid (like position-visibility's trigger, but with options besides just hiding the element).

Instead of focusing customizability efforts around niche positioning use cases, we’d love to see more work towards making it possible to specify pointers, which seem so common we worry about the use case coverage of an anchor positioning feature without some way to achieve them.

By this do you mean anchoring to the mouse pointer, or having, like, popups with a little arrow on their edge pointing to the anchor, or something else?

We understand the rationale for using anchor-name instead of HTML ids (e.g. scoping, markup edits), but we are concerned that new properties are being added to name elements in (likely) the same way. For example, there is currently container-name, which also names an element. Is it common for these names to need different identifiers? If so, would it make sense to have a shorthand (e.g. name) of all CSS properties that name an element?

The big design consideration here is not whether or not they'd use different names, but whether they'd be set by the same code/authors at all. CSS in general has a problem with "additive" features, where two different selectors might want to set different (combinable) keywords in a single property. (This is one of the reasons we designed the individual transform properties, for example; it helps avoid clashes in at least some cases.)

If we had a single property that handled the definition of names across multiple features, it's almost certain authors would run hard into this issue. The current design (across several specs) avoids that when the different bits of code are using different features, at the cost of repetitious design. I think the status quo is the best option at the moment, including as we design future features that need more names.

That said, nothing stops us from adding a shorthand covering them all in the future.

anchor-scope does not appear in the explainer(s) and does not appear to feature in the algorithm that finds an anchor.

It was not part of our initial implementation, but it's being added now.

The effect of the property is well-defined in its description, and imo didn't need to be restated in the anchor-finding algo. Hm, you are right that it would aid clarity to mention it in the "acceptable anchor element" definition, tho. I'll fix that.

The algorithm for finding anchors is quite complex. That is not necessarily an issue, internal complexity is often necesary to avoid external (author-facing) complexity, however the motivation needs to be explained more.

There already was an explanatory note there, but it was at the end of the algorithm and not the most obvious. I've shifted it to a more visible spot and slightly rephrased it so it should read a little better.

Some other notes on the current syntax:

  • Properties that are unlikely to ever apply to position schemes beyond anchor should make this clear by being prefixed via position-anchor-* (or just anchor-*), not just position-*.

"Anchor" isn't a positioning scheme on its own; it's just a new capability added to existing positioning schemes. All the position-* properties defined in this draft do, in fact, apply at least somewhat even if nothing anchor-related is used at all. (They're generally less useful, as both 'position-visibility' and 'position-try-' are about managing overflow, and that's mostly a concern when using anchors due to the substantially more dynamic placement options, but they still can* be used without any anchoring going on.)

(Well, not position-anchor, but that already satisfies your comment.)

  • position-visibility affects visibility, not position. Therefore, we wondered if a visibility value may have been more appropriate.

The feature may not affect position, but it is based on it - all the non-initial values depend on the element being a positioned element. It's possible to have a property with some keywords that apply generally and some that apply only for certain types of elements, but it's often a slightly clumsy design as you have to define what the specialized keywords fall back to on invalid targets.

  • Why is this layered on top of position: absolute and position: fixed rather than a new position: anchor behavior?

Putting aside the bad design of the position property itself for a moment (the rel/sticky behaviors and abs/fixed behaviors are completely distinct layout modes, and shouldn't have been mixed), there was simply no need. This is an upgrade to the "absolute positioning" feature, which is triggered by absolute or fixed. It adds new options to that layout mode, but doesn't need or want to fundamentally change how it works.

  • It's not clear what the implications of using position: absolute vs position: fixed are. Perhaps the behavior difference would be better handled by an anchor-specific property.

The behavior difference is solely what containing block is used. There was no need to invent a new property duplicating that behavior switch. The CB has some implications on how anchoring works, such as what elements are valid anchors, but those implications are fully captured by the CB choice itself, and so absolute vs fixed is already the most appropriate way to do that. If we want any more CB options, we'll want them generally for positioned elements, so it wouldn't be an anchor-specific feature either.

LeaVerou commented 1 month ago

We think the simple cases of a popup that merely shifts or flips its position be always visible should not require a separate @position-try rule or writing out the logic, authors should be able to declare intent and have things Just Work™.

That is indeed already possible, by using the flip-* keywords in position-try-options. You only need to reach for the @position-try rule if you're doing non-trivial alternatives.

These seem to revolve around flipping, while offset would still require @position-try. Is that not the case?

We understand the rationale for using anchor-name instead of HTML ids (e.g. scoping, markup edits), but we are concerned that new properties are being added to name elements in (likely) the same way. For example, there is currently container-name, which also names an element. Is it common for these names to need different identifiers? If so, would it make sense to have a shorthand (e.g. name) of all CSS properties that name an element?

The big design consideration here is not whether or not they'd use different names, but whether they'd be set by the same code/authors at all. CSS in general has a problem with "additive" features, where two different selectors might want to set different (combinable) keywords in a single property. (This is one of the reasons we designed the individual transform properties, for example; it helps avoid clashes in at least some cases.)

Fair, it seems that a shorthand would alleviate this, while authors could still use the longhands for composability?

Also, an idea that was proposed during the review was what if instead of anchor-name one could specify a (subset of) relative selectors? That seems much more flexible, and arguably easier to understand than the current complex algorithm of finding the anchor, and the mechanism could be reused in the future by other properties that need to reference elements (e.g. element()). Though it would also be a much more substantial undertaking. Perhaps the feature could be designed in a way that allows for something like this to be retrofitted in later?

  • position-visibility affects visibility, not position. Therefore, we wondered if a visibility value may have been more appropriate.

The feature may not affect position, but it is based on it - all the non-initial values depend on the element being a positioned element. It's possible to have a property with some keywords that apply generally and some that apply only for certain types of elements, but it's often a slightly clumsy design as you have to define what the specialized keywords fall back to on invalid targets.

Presumably they would simply have no effect if the element is not positioned? This is not the first time this pattern is employed… Yes, it typically causes author confusion, but so does having to hunt down multiple properties to figure out why an element is or isn't displaying…

tabatkins commented 1 month ago

These seem to revolve around flipping, while offset would still require @position-try. Is that not the case?

Ah, sorry, missed the full implication of the sentence. It depends on what you mean by shifting. You can specify a different inset-area inline in position-try-options, so if that's all you need to do (and often, it will be) there's still no need to use @position-try. As we discussed in this week's CSSWG call, too, if what you want is "slightly shift off your specified alignment to avoid overflowing", that was deferred from this level while we figure out a general solution for position-shifting (and Elika has ideas for how to achieve that, in https://github.com/w3c/csswg-drafts/issues/10316)

Fair, it seems that a shorthand would alleviate this, while authors could still use the longhands for composability?

Right, and that's something we can safely add in the future, without having to block any individual specs defining names today.

[selectors instead of names]

That's potentially doable, sure, and would indeed subsume a number of cases that today are best done via anchor-name+anchor-scope. The syntax is open for it, we'd just need a selector() function or something. Definitely doable in the future.

Yes, it typically causes author confusion, but so does having to hunt down multiple properties to figure out why an element is or isn't displaying…

Right, it can cause confusion if some values don't work for no immediately-obvious reason; we definitely do this when we need to, but try to avoid it when we can. On the other hand, if you're wondering why an element isn't visible, and you inspect its styles and see a position-visibility property... that seems as obvious as seeing visibility, imo. ^_^

torgo commented 3 weeks ago

Hi @abatkins - Thanks for engaging with us here. We are just wondering if the feedback above has been discussed in the CSS working group?

mfreed7 commented 3 weeks ago

@tabatkins just to make sure they see the message above. And @abatkins, sorry for the mention. 😄