w3c / csswg-drafts

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

[css-anchor-position-1] Allow anchor references to match names in outer tree scopes #9408

Open xiaochengh opened 12 months ago

xiaochengh commented 12 months ago

Anchor names and references are both tree scoped.

Currently, we only allow exact matches. In other words, anchor names can only be referenced in tree scopes where they are defined.

Proposal: allow anchor references in inner tree scopes to match names defined in outer tree scopes (like how we match at-rule names).

Reason:

The current way it's matched can be problematic according to feedback (@e111077), when we want elements in an inner tree scope to anchor to external anchors. For example:

<div style="anchor-name: --anchor-1"></div>

<custom-element>
  #shadow-root
    <div class="anchored"></div>
    <style>
      /* Try to anchor the element against --anchor-1 but can't */
      .anchored { inset-block-start: anchor(??? top); }
    </style>
</custom-element>

Currently the only proper way to anchor the .anchored element against --anchor-1 is to use a ::part selector in the outer tree scope. And this won't work if .anchored is further nested in another shadow tree, because we don't allow ::part selectors to chain.

If we allow inner tree scope's references to match outer tree scope's definitions, then we can simply put anchor(--my-anchor-1 top) in the inner tree scope's style sheets, regardless of how deep the inner scope is.

(In retrospect, we made anchor name use exact tree scope matching because we didn't know about use cases and wanted to keep implementation simple, see https://github.com/w3c/csswg-drafts/issues/7916#issuecomment-1287436800; now we have use cases)

xiaochengh commented 12 months ago

Minor correction: when .anchored is in a deep-nested tree scope, we can still style it with ::part, but also need a long chain of [exportparts], which is still quite a burden to maintain.

Also, with the proposal, we can solve everything nicely without breaking encapsulation regardless of how deep the shadow tree nesting is, by passing the anchor name into the custom element via a custom property. For example:

<custom-element style="--custom-element-anchor-name: --anchor-1">
  #shadow-root
    <div class="anchored"></div>
    <style>
      .anchored { inset-block-start: anchor(var(--custom-element-anchor-name) top); }
    </style>
</custom-element>
e111077 commented 4 months ago

Seeing how chrome is about to ship, is there any possible way to get shadow dom support at all? This makes css anchor positioning effectively useless for web components. cc @tabatkins who touched this last for routing

KonnorRogers commented 4 months ago

+1 for ShadowDOM support.

we have an internal utility called <sl-popup> in Shoelace that uses Floating UI currently.

It supports setting an arbitrary anchorElement which could live in any root. As I understand currently, Anchored Region would only allow us to set anchors in the same shadow root as the anchor from the popup utility.

Without cross-root anchoring, it feels like anchored region would do very little to replace our current usage of Floating UI because it cannot accept arbitrary anchors. or at the very least, anchors from "parent" DOM trees.

Westbrook commented 4 months ago

This issue seems so much like accessibility with shadow roots, if it's not included by default it could be years before it's possible to get enough weight behind a productive solution. We should take care not to ship something to the web platform that doesn't support all of the web platform! To that end, it would be great to find some API that allowed these two examples to delivery roughly the same: https://codepen.io/Westbrook/pen/qBGEbdE

Luckily there is some prior art to lean on here.

Take for instance arialabelledbyelements a DOM property that allows an element to be labeled by an array of element references that are in the same shadow tree or an ancestor tree. We may be waiting on long-rumored Blink and Mozilla implementations of this, but they are getting close and will do some great things to accessibility with shadow roots. Can we find a reciprocal version of this for anchor references?

Some things that I could certainly see as important to work out in this context:

Unmanaged sharing I'm not quite sure how this could be possible but for anyone interested in going down this path.

I'm not sure there are good or performance answers to these questions but they should be discussed well before something in this realm hits "stable".

Managed sharing What would this look like? The main goal would seem to prevent anchor name collision, but this could also manage the distance to which a browser would need to go to resolve an anchor name.

I look forward to seeing this make its way into the larger conversation before any "stable" API is marketed to the larger web development community as ready to go!

vospascal commented 4 months ago

if it's not included by default it could be years before it's possible to get enough weight behind a productive solution. We should take care not to ship something to the web platform that doesn't support all of the web platform!

i agree on this, having to think about exceptions / and caveats or not even being able to use something because its excluded from ShadowDOM is far from ideal.

michaelwarren1106 commented 4 months ago

another +1 for considering shadow roots for anchor positioning.

as a larger issue, is there a recommended way that shadow root / cross root functionality can become part of the requirements of new (existing too?) proposals?

it seems like a fairly common concern that shadow roots be taken into account for new css features.

asyncLiz commented 4 months ago

A strong +1 to echo this requirement. Without full shadow dom support, CSS anchor roots are a no-go for Material Web Components. 😞

filimon-danopoulos commented 4 months ago

I would be very sad to see new features that I can't use as a web component author. The design system I work with has a popout feature similar to the Shoelace one.

astearns commented 4 months ago

@tabatkins can you comment on your reasons for moving this issue to level 2? Is this something we could bring back in to level 1?

trusktr commented 4 months ago

What about new functions? Something like

.positioned-notice {
    position-anchor: crossrootall(--anchor-el);
}
.positioned-notice2 {
    top: anchor(crossrootupward(--anchor-el));
}

where here crossrootall finds the nearest matching anchor in this order:

and where here crossrootupward finds the nearest matching anchor in this order:

and imagine crossrootdownward.