Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
xiaochengh opened this issue Sep 26, 2023 · 10 comments

Comments

@xiaochengh
Copy link
Contributor

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 #7916 (comment); now we have use cases)

@xiaochengh
Copy link
Contributor Author

xiaochengh commented Sep 26, 2023

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
Copy link

e111077 commented May 11, 2024

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
Copy link

+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
Copy link

Westbrook commented May 11, 2024

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.

  • if there is some sort of sharing between trees, and it is not managed in some way, which tree "wins"? e.g. if there is an anchor-name: --anchor-1 in the local DOM tree and an anchor-name: --anchor-1 in the parent DOM tree, which gets leveraged?
  • similar if this sharing is non-managed, would it be expected that CSS would need to walk up ALL DOM trees to possibly resolve an anchor?

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.

  • is a CSS custom property enough to pass an anchor name across multiple shadow trees? See example.
  • can anchor-scope accept some sort of magic "parent scope" key?
  • could an element with a shadow root define how many trees away an anchor name could be searched for?
  • do we need something like downward facing exportparts to make them available to child trees?
  • the reference target proposal may provide prior art here, as well

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link
Member

@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
Copy link

trusktr commented May 13, 2024

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:

  • check the current root
  • if not found, check all roots below the current root in tree order
  • if not found, check the next root upward
  • if not found, check all roots downward except the one we just went upward from
  • if not found, repeat the previous step until found, or until there are no more upward roots

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

  • check the current root
  • if not found, check the next root upward
  • if not found, repeat the previous step until found, or until there are no more upward roots

and imagine crossrootdownward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests