-
Notifications
You must be signed in to change notification settings - Fork 690
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] Evaluate cascade order of ::slotted and global styles in the same conditions #6466
Comments
I also found in this explanation that don't talk about specify. https://www.w3.org/TR/css-cascade-5/#encapsulation-contexts
To compare 2 declaration you don't take only the source of the encapsulation context. Specificity of the selector should be evaluated too. |
I can definitely see the issue here but I'm not sure I agree with the solution, in part, because it results in potential breaking changes for existing code that has already worked around it. As I understood it, the slotted content was meant really to be a pointer to light DOM and not necessarily meant to change its styling. That we can force a win by adding Perhaps there's a middle ground. An opt-into level specificity by the shadow DOM styles? |
I know it could break the backward compatibility, and I don't suggest it as solution. I opened the issue to find a way to improve the DX and be able to use the light DOM comfortably. The problem with important is that once you use important in your component styles they can't be override externally. Then you have to choose between weak components styles (without important) or fixed styles (with important). In the weak case, just with a From my point of view, it's impossible to build a design system using light DOM because you have to put We have to find a solution to make light DOM "stylable" easily. |
One of the main reasons why I dislike Imagine a web component and an extension of it: class FooExt extends FooEl {
styles() {
return `
${super.styles}
::slotted(#foo) {
border-color: green !important;
}
`;
}
} The border color will only be green if this CSS part is later in the cascade than super.styles, because they both have |
Currently, the only way of working around this is with IMO current behavior is pretty broken. However, I wouldn't mind opt-in, if it's something simple like |
You can override them externally with the use of |
@castastrophe you can't. Take a look the Demo provided, as you can see "Slotted paragraph with !important in the internal and external styles 🙈🙈" is in green instead of pink trying to override styles with important in the external stylesheet. https://studio.webcomponents.dev/edit/eQFJPlOvQMIuUZj71ohV/README.md?p=README.md |
@jorgecasar Let's simplify your example: https://codepen.io/castastrophe/pen/KKoXGQo There seems to be custom styles applied to READMEs in the environment you shared and that interferes with the discussion of specificity and scope. Typography styles are complex because they already penetrate the shadow DOM; they're some of the few properties that do. I want to focus the example on the use of I've reduced the component's template to:
Now to override, we use:
I've used a simple selector here on my page so you can see that the specificity of the selector isn't as important either: just the component and the class we're targeting. You can see in my provided link that this works as I would expect. |
I want to note, I would not expect a |
I find a fairly major flaw in your original proposal though to be honest which is this statement |
Then, we're pretty locked:
Then I think the point here is to have a useful |
Hmm slots and content projection are a very common use case and the exact contract between a slot and its slottable (that which is slotted in) is ambiguously defined imo, it's not clear to what extent the slottable should be influenced by the component. In my personal experience authoring and consuming components, the answer is "a fair amount". If you have some widget in which you can slot DOM, it does happen from time to time that the widget relies on needing to functionally style what's projected into it, in theory ::slotted is for this purpose but currently it just doesn't do that job well at all. It's not about a global stylesheet vs encapsulated component distinction |
I humbly suggest that there should be some sort of new mechanism to enable specific styles to slotted components without breaking the current behavior. Having to add "!important" on all ::slotted rules is a very ugly work-around. Web components are a breath of fresh air for improving old code-bases with cool new functionality, but it's also the old code-bases which tend to have a lot of weird global CSS rules. |
When my boss asked me to override an input with |
Retitling because it was clear in #6867 that the problem with slotted is not the specificity but the cascade order rules. It seems the reason people don't hit this with :host is because those are not builtins. We discussed a bit "what would the ideal cascade order of these be". It seems there was a desire of just letting specificity fight as usual, and only then sort by tree (that is, make the sort key, ignoring cascade layers, something like Another less breaking alternative would be something like an "important layer" kind of concept, where you'd sort by layer importance before sorting by tree. Cc @LeaVerou @keithamus @rniwa @tabatkins |
We discussed this a fair bit during breakouts with @emilio @rniwa during TPAC, and we'd love to hear from @tabatkins and @fantasai. Currently, In my experience, you typically want more granularity than the all-or-nothing of the current situation: you don’t want component styles to override author styles specifically targeting those elements, but also you don't want generic catch-all author rules like Intuitively, it seems that the kind of cascade order that would make sense here is to treat encapsulation context at the same precedence level as source order. @emilio's proposed "important layer" concept seems useful in its own right, but would have similar problems as Fixing this may require an opt-in of some sort (possibly as a |
I want to focus on this particular point. In #10094, I've suggested introducing (something like) Even if that doesn't solve all use-cases, I think it would be helpful. I've been using this simple example to demonstrate the problem: |
It seems a little strange to me that cascade layers (currently sorted after context) would have an internal mechanism for jumping ahead of context in the sort order. But I agree that something layer-like would be useful here. And in this case, I think specifically named options (like the mentioned |
Any solution that expects the host page to just "behave" is not workable when you're building components that need to work in any page. :/ |
It can be both, right? A way to deprioritize "outer" styles, and a way to prioritize "inner" styles. In #10094 (comment), I've suggested a new Hypothetical example<head>
<style>
/* this will cascade before all shadow-roots, requiring no opt-in from shadow-roots */
@context(first) {
*, ::before, ::after {
box-sizing: border-box; margin: 0; padding: 0;
}
}
</style>
</head>
<body>
<my-component>
<div>Slotted</div>
<template shadowrootmode="open">
<slot></slot>
<style>
/* this will cascade after all outer contexts, requiring no opt-in from host */
@context(last) {
:host { padding: 4px; }
:slotted(*) { margin: 4px; }
}
</style>
</template>
</my-component>
</body> The exact syntax and names are debatable of course. The important thing I want to highlight is that this is a concern that lies above layers, specificity, source order, etc. It doesn't make sense to solve it at the level of specificity. It would be more appropriate to solve it using context or a new concept that sits above context-scoped layers. |
Description
According to the spec 3.2.4. Selecting Slot-Assigned Content: the ::slotted() pseudo-element:
In a previous conversation (#1915 (comment)), it was clear and the solution proposed is to use
!important
but I think that the current solution is a bad Developer Experience when you apply it to real life.Following the definition,
::slotted(h1)
should have a 0-0-2 specificity andh1
just 0-0-1. Then::slotted(h1)
should win without!important
. But it seems they don't fight in the same arena and!important
is required.Example
Here is an that explains better the use case. Taking this HTML as a base. We want to use slots to take the advantage of HTML declaration, for example for SEO reasons.
We would like to have some generic styles for headings and paragraphs but the web component would like to restyle them using the
::slotted()
pseudo-element. As there are other h1 in other pages withoutfancy-hero
wrapper, we have some generic styles like this:Then the color of my
fancy-hero h1
changed to#333
. The only way to preserve thefancy-hero h1
style from inside thefancy-hero
is by applying!important
But once you do that, it's impossible to change from outside, to make my component customizable. And the only way is to use custom properties like this:
Then I can customize my
fancy-hero
adding this styles into the global styles:Extrapolating this use case to a complex component with multiple slots and much more properties, it's not viable. At this point we have two options:
!important
and declare custom properties in all styles properties of my component.Proposal
I would like to propose that shadow DOM styles fight with the global styles in the same conditions. The cascade should be applied independently where the styles are defined.
Coming back to the specificity definition
::slotted(h1)
should have a 0-1-1 andh1
0-1-0. Then::slotted(h1)
should win without!important
. This allows developers to create custom elements more reusable and easy to customize without dealing with thousand of custom properties and fill the code with!important
in all properties.Demo
https://webcomponents.dev/edit/eQFJPlOvQMIuUZj71ohV/README.md
The text was updated successfully, but these errors were encountered: