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-cascade] How do Cascade Layers interact with Shadow DOM #4984

Closed
mirisuzanne opened this issue Apr 22, 2020 · 3 comments
Closed

[css-cascade] How do Cascade Layers interact with Shadow DOM #4984

mirisuzanne opened this issue Apr 22, 2020 · 3 comments

Comments

@mirisuzanne
Copy link
Contributor

mirisuzanne commented Apr 22, 2020

This is in relation to #4470 custom-origin proposal (now in Cascade 5), and the Shadow DOM cascade rules. To quote the original transcript:

emilio: Shadow DOM introduces a stack of origins; introducing this naively makes it a matrix, which is harder.

@tabatkins
Copy link
Member

To elaborate on this issue:

As you know, Shadow DOM isolates elements from outside styling - by default, the stuff in a shadow isn't visible to selectors in the outer document, and this applies further as you nest, so each component's DOM is only styled by the stylesheets in the same shadow tree. So far, simple.

But we have ::part(), which allows outer scopes to apply styles to elements in shadows. So we have to decide which rule wins when there's a conflict. We rejected just using specificity as normal, since the outer and inner scopes aren't necessarily designed together, and thus have no good way to avoid annoying interleaving errors with specificity.

Instead, we decided to add a new layer, between specificity and origin: scope. In the non-important origins, outer scopes win over inner scopes; ::part() styles will always defeat the component's own styles, because it's assumed that using::part() for customization should be more considered higher-priority than setting up default always-on styles inside the component. The ordering is reversed in !important origins; ::part() styles lose to the component's own styles, because presumably the component knows the required bits of its style better than the outer page does, and so can guard them by making them !important.

A given element can have more than two scopes competing over it, too: if a component nests an inner component, and the inner component exposes a part, then the outer component forwards that part onto itself, then you can have: (1) the inner component's own style, (2) the outer component using ::part() to style the inner component's element, and (3) the outer page using the forwarded ::part() to also style the inner component's element. And all this has the reverse cascade in !important styles, too; potentially six different origin+scope combinations to puzzle thru when deciding which rule should win.

(Effectively, we're just adding arbitrary additional origins, arranged in the standard way based on a priority-of-constituencies approach. Just like author styles win over user styles, because they're assumed to be more specific and thus more likely to be correct in a given instance, styles from ::part() win over styles from the component. And !important inverts the relationship for the same reason.)

So! If custom origins uses the actual Origin machinery, then this increases all that complexity. Rather than 2N origin+scope combinations (where N is the depth of the shadows), we'd have MN combinations (where M is the total number of origins, at least 2). That gets complicated, both for authors and for implementations. It would also mean that scope is no longer a strong determiner of the winner: a higher-origin style in a component would beat a lower-origin style from ::part(); it's not clear if authors would expect this or not. (The "shadow scope is effectively additional origins" idea becomes very muddled; the "scoping origins" get duplicated into every custom origin, and you get a probably-confusing interleaving of styles. I think it matches author mental models better for scopes to be "more important" in the cascade than custom origins are, so a component user can confidently predict whether their ::part() styles will win over a component's own styles.)

If we instead do something like I suggest in #4981 (comment), moving this functionality into a new specificity category, we avoid the complexity problem; we retain just 2*N scope+origin combos. We also maintain the current "::part() beats component styles" behavior, which I suspect is better for authors.

Elika does bring up some counter-points to that idea, tho, so more thought is needed.

@mirisuzanne mirisuzanne changed the title [css-cascade] How do custom origins interact with Shadow DOM [css-cascade] How do Cascade Layers interact with Shadow DOM May 28, 2020
@mirisuzanne
Copy link
Contributor Author

The current proposal in Cascade 5 Editor's Draft is that Layers are resolved after Encapsulation Context (roughly the same as "a new specificity category").

Even if that resolves the issue above, it still leaves open a question about global and shadow-scoped layers using the same name:

  • Are those the "same" layer in terms of layer-ordering? Can outer stylesheets re-order the layers of a shdow-DOM element? (I think this is probably not a good idea by default)
  • Are there use-cases for accessing a global layer from shadow-scoped styles, or vice versa? Would we need a syntax for that?

@mirisuzanne
Copy link
Contributor Author

Moved the remaining question into a new issue, and closing this general question as resolved by the spec.

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

2 participants