Re: [csswg-drafts] [css-mixin-1] How does Shadow DOM influence mixin lookup? (#12671)

The CSS Working Group just discussed `[css-mixin-1] How does Shadow DOM influence mixin lookup?`, and agreed to the following:

* `ACTION: fantasai to talk to Antti and get back a Webkit position on this.`
* `SUMMARY: Desire to have mixins be loosely matched, but concerns about performance and implementation complexity.`

<details><summary>The full IRC log of that discussion</summary>
&lt;fantasai> TabAtkins: @mixin defines a named rule, like other global name-defining rules<br>
&lt;fantasai> TabAtkins: I believe the right answer is to lean on our existing functionality for scoping<br>
&lt;fantasai> TabAtkins: if you're in a shadow DOM stylesheet, we look in that shadow DOM, then walk up the tree looking for the name<br>
&lt;lea> q+<br>
&lt;fantasai> TabAtkins: just like font faces<br>
&lt;fantasai> TabAtkins: So that the shadow DOM can reference its own font faces safely, but also reference the parent DOM's fonts when needed<br>
&lt;castastrophe> +1<br>
&lt;fantasai> TabAtkins: Similarly, would be able to define a mixin in the parent scope and use them into the shadows<br>
&lt;kbabbitt> +1<br>
&lt;miriam> +1<br>
&lt;emilio> q+<br>
&lt;astearns> ack lea<br>
&lt;fantasai> PROPOSED: Mixins use loosely-matched tree-scoped references<br>
&lt;fantasai> lea: +1<br>
&lt;fantasai> lea: very strong use cases in the community for this behavior<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> emilio: Main concern with this is that it makes the global styles affect rules of shadow trees.<br>
&lt;bkardell> yeag<br>
&lt;lea> q+<br>
&lt;bkardell> yeah*<br>
&lt;fantasai> emilio: Mixins are weird because they do introduce rules into a scope. They can have child rules, selectors, etc.<br>
&lt;fantasai> emilio: So they get applied at a totally different time than things that inherit.<br>
&lt;fantasai> emilio: Issue here is that shadow DOM styles are generally isolated from the rest of the page<br>
&lt;fantasai> emilio: Blink is the only engine that has a global system<br>
&lt;fantasai> emilio: Data stored because not just that stylesheet, but also all the parent scopes.<br>
&lt;fantasai> emilio: I think it's also an issue for Webkit<br>
&lt;fantasai> lea: To be clear, not looking into a shadow, only looking up<br>
&lt;fantasai> lea: There's questions around implementation difficulty, but strong signals from author community that this is what they want.<br>
&lt;fantasai> lea: It won't inadvertently affect the shadow's styles, because we prioritize the inner scopes.<br>
&lt;castastrophe> q+ how do keyframes work for shadow trees? Keyframes do inherit between shadow trees<br>
&lt;fantasai> emilio: That's my main concern, would like to hear from WebKit wrt implementability and performance.<br>
&lt;astearns> ack lea<br>
&lt;fantasai> castastrophe: For @keyframes, is that an implementation approach we could re-use? Is there a difference?<br>
&lt;fantasai> emilio: They are totally different. Keyframes get looked up after the element has been styled.<br>
&lt;fantasai> emilio: They don't affect other rules inside that stylesheet set.<br>
&lt;fantasai> lea: What if we resolve that the desire is for them to inherit down, but pending implementation info.<br>
&lt;fantasai> emilio: The reason why it works in Blink is because they don't have per-shadow-tree rule-data<br>
&lt;fantasai> emilio: But that has other implications like site invalidation being slower, etc.<br>
&lt;fantasai> emilio: It's very easy for us to detect when a change is a no-op, for example.<br>
&lt;fantasai> astearns: Any alternative? Author feedback seems to be pretty clear.<br>
&lt;castastrophe> q+<br>
&lt;fantasai> lea: One of the ways authors are working around the problems currently is that authors walk the stylesheets manually and pull them in. Wouldn't that be slower?<br>
&lt;fantasai> emilio: You're not doing that automatically all the time.<br>
&lt;fantasai> TabAtkins: Would this objection also apply to custom media?<br>
&lt;fantasai> emilio: Yes.<br>
&lt;fantasai> TabAtkins: Seems similar to question of "what do we need to restyle" for class changes, which, use bloom filters<br>
&lt;fantasai> emilio: It's totally different. [gestures]<br>
&lt;fantasai> emilio: Bloom filter is for ancestor selectors. you're not in this subtree, don't need to look.<br>
&lt;kbabbitt> q+<br>
&lt;fantasai> TabAtkins: Concern is that it would be expensive to re-evaluate all the shadow trees.<br>
&lt;fantasai> TabAtkins: but if you know ahead of time, they're not using the mixin, then you don't have to check.<br>
&lt;fantasai> emilio: ...<br>
&lt;fantasai> emilio: It's a significant implementation difficulty that Blink doesn't suffer<br>
&lt;astearns> ack castastrophe<br>
&lt;fantasai> castastrophe: From my own work and talking to web components CG, this is a huge blocker.<br>
&lt;lea> +1 castastrophe<br>
&lt;fantasai> castastrophe: Can't use mixins the way they want.<br>
&lt;fantasai> castastrophe: If there's a way to create some kind of commitment that we're not updating a mixin once it's defined, or something...<br>
&lt;fantasai> castastrophe: We already have perf issues with massively nested shadow DOM anyway. Would rather take the perf hit than try to solve all these problems with JS.<br>
&lt;lea> q?<br>
&lt;lea> q+<br>
&lt;fantasai> emilio: Is that perf issue specific to Blink?<br>
&lt;fantasai> castastrophe: Lots of DIV soup.<br>
&lt;fantasai> castastrophe: Adobe is a great example. Photoshow is so many nested components.<br>
&lt;fantasai> castastrophe: They're trying to do crazy things with theming.<br>
&lt;bkardell> s/Photoshow/Photoshop<br>
&lt;fantasai> castastrophe: But if cut out those use cases, then not getting out of JS<br>
&lt;fantasai> emilio: So the issue is the tree size?<br>
&lt;fantasai> emilio: Is it about depth of the *shadow* tree or depth of the tree?<br>
&lt;fantasai> castastrophe: They claimed it was shadow trees, but I don't have access any more.<br>
&lt;fantasai> castastrophe: We had a theming base class. Every component was inheritng that base class. Mixins would solve that, could use loosely scoped mixins would reduce the need for these base classes.<br>
&lt;astearns> ack kbabbitt<br>
&lt;fantasai> kbabbitt: Shadow-piercing combinator?<br>
&lt;fantasai> TabAtkins: Had one, backed it out.<br>
&lt;fantasai> kbabbitt: If I used that in a selector in a mixin, that would effectively ... would that be different perfwise than this?<br>
&lt;fantasai> TabAtkins: that's part of why we dropped it<br>
&lt;fantasai> emilio: Not going to formally object, but I think it's very foot-gunny if you have mixin use deep in your tree<br>
&lt;fantasai> emilio: need to rebuild everything all the way up<br>
&lt;fantasai> emilio: Also what does that do to the encapsulation of the shadow DOM?<br>
&lt;fantasai> emilio: We try to not expose the structure of the shadow DOM to the light, but you can put any selector you want in there.<br>
&lt;fantasai> TabAtkins: because it would be the shadow actively using the mixin, it's ok<br>
&lt;fantasai> castastrophe: Is the main concern that it introduces selectors? Or is also an issue of properties?<br>
&lt;fantasai> emilio: Anything affecting nested CSS rules causes this issue.<br>
&lt;fantasai> emilio: Fact that you can inject arbitrary selectors there, and they can affect the shadows.<br>
&lt;fantasai> emilio: Is what you want just inheriting blocks of properties?<br>
&lt;Kurt> q+<br>
&lt;fantasai> castastrophe: That's a big chunk of it. Have a button and want to extend the styles. But not all of them.<br>
&lt;fantasai> emilio: But that include things like &amp;:hover?<br>
&lt;fantasai> castastrophe: yes<br>
&lt;fantasai> emilio: so it's the same issue<br>
&lt;astearns> ack lea<br>
&lt;fantasai> lea: One thing was around use cases, castastrophe mentioned one -- if you want to have a button in your shadow DOM that you want to style like buttons on the main page<br>
&lt;fantasai> lea: right now very challenging. But with mixins can just use a mixin and pass it in.<br>
&lt;emilio> q+<br>
&lt;fantasai> lea: One thing to say, sounds like problem is having data structure about what shadow roots exist on the page<br>
&lt;fantasai> emilio: we have that, it's just slow to update<br>
&lt;fantasai> lea: I mean an O(1) data struct<br>
&lt;castastrophe> The primary use-case for web components as I see it is core mixins definitions at the document root and web components extending those mixins inside the shadow dom<br>
&lt;fantasai> lea: :has-shadow was proposed. Custom media. Tree walking shadow roots. Lots of use cases for it.<br>
&lt;fantasai> emilio: It's O(N) structure, but N can be really big.<br>
&lt;fantasai> lea: Performance can always improve. But behavior you're stuck with.<br>
&lt;fantasai> lea: This isn't a case where it doesn't matter that much. There are really strong use cases.<br>
&lt;astearns> ack Kurt<br>
&lt;Kurt> https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ShadowDOM/explainer.md<br>
&lt;fantasai> emilio: Challenge the use cases.<br>
&lt;fantasai> Kurt: Here's a proposal I'm working on for shadow root adopted stylesheets.<br>
&lt;fantasai> Kurt: It's efficient because the stylesheet instance is shared between them.<br>
&lt;fantasai> Kurt: This avoids some of the perf issues.<br>
&lt;fantasai> Kurt: I'm curious if there are edge cases this doesn't cover.<br>
&lt;fantasai> emilio: That's a much more explicit mechanism.<br>
&lt;fantasai> castastrophe: This is a great feature, but different from what we're talking about.<br>
&lt;fantasai> castastrophe: Way web components engineers want to use styles is for parts of styles.<br>
&lt;fantasai> castastrophe: for parts of shadow dom, style them<br>
&lt;fantasai> castastrophe: opting into certain features, stacking them in a way that's readable<br>
&lt;fantasai> castastrophe: You're controlling and limiting what the component does.<br>
&lt;fantasai> castastrophe: Right now we load the entire stylesheet for the entire style system when all we want is the button.<br>
&lt;fantasai> castastrophe: We're doing in excess<br>
&lt;fantasai> castastrophe: Mixins got community excited was the idea of being able to pull in just want we need, and combine parts of a system inside different shadow DOMs<br>
&lt;lea> a+<br>
&lt;lea> q+<br>
&lt;fantasai> Kurt: So instead of applying entire sheets, it's applying specific rules down one at a time.<br>
&lt;fantasai> Kurt: Could those sheets be broke up and used this way?<br>
&lt;fantasai> castastrophe: Possibility of dynamicism. So many different ways to do a button. If you're building a larger component, and it has a button in shadow DOM. Rather than doing a web component button, do a web component stylesheet definitions, and when you have a button in your markup (like a dialog or toast or whatever), you extend the styles for that specific use case.<br>
&lt;fantasai> castastrophe: Rather than having a separate component that does all the things for the button.<br>
&lt;lea> q?<br>
&lt;fantasai> Kurt: could that be put in a separate sheet and loaded?<br>
&lt;fantasai> castastrophe: But then you'd have a ton of tiny little sheets.<br>
&lt;fantasai> castastrophe: We're doing button. And then butotn with this. And primary button. And link button. These are all different variants.<br>
&lt;fantasai> castastrophe: if you just need to style something, pull them in with CSS<br>
&lt;fantasai> castastrophe: If you're bulding a new component, different.<br>
&lt;fantasai> castastrophe: This is exciting because we can mix and match. But not going to scale if we need to load 20 parts as style sheets.<br>
&lt;fantasai> castastrophe: Back to photoshop. It's one big component, and a bunch of tiny ones.<br>
&lt;fantasai> castastrophe: Want to remove the tiny ones and just use mixins.<br>
&lt;fantasai> castastrophe: That's a perf gain.<br>
&lt;fantasai> Kurt: Differentiator here is level of granularity for the styles. Even if small and many, it's efficient.<br>
&lt;lea> q?<br>
&lt;fantasai> emilio: I like to see some of the use cases written. How would you write this with mixins?<br>
&lt;fantasai> emilio: A lot of the things would end up being as efficient or more efficient if you shared the rules.<br>
&lt;fantasai> emilio: Use case: I want all my design tokens. Pulling that via mixin is terrible idea. Applying to thousands of elements instead of inheriting from one.<br>
&lt;fantasai> emilio: Re-using adopted stylesheet mechanism or whatever, explicit that "these are the rules I want in my tree".<br>
&lt;fantasai> emilio: but mixins inherit through shadow trees is implicit<br>
&lt;castastrophe> Another theming use-case might be the theming layering - "base brand" + "product brand" overrides<br>
&lt;lea> ack emilio<br>
&lt;fantasai> emilio: You might accidentally load new stylesheet that has a mixin, which has an impact on page load that you didn't expect.<br>
&lt;fantasai> emilio: It's a pain implementationwise, and footgun performance-wise.<br>
&lt;astearns> ack lea<br>
&lt;fantasai> lea: First, this is not a session about @sheet. That was presented at TPAC and some pushback on that.<br>
&lt;fantasai> lea: What's important about mixins is that they decouple selectors from styles.<br>
&lt;fantasai> lea: @style re-uses chunks as styles as they are. No scoping. No parameterization<br>
&lt;fantasai> lea: You can have a single mixin that you can pass to multiple components.<br>
&lt;fantasai> lea: Ideal is to mix and match components from different sources.<br>
&lt;fantasai> lea: You can define your button mixin, and alias it for differnet components.<br>
&lt;fantasai> lea: but with @sheet you need a very tight couping between your styles and the component. But you don't control the component.<br>
&lt;fantasai> lea: The benefit of mixins is being able to communicate with minimal handshake.<br>
&lt;castastrophe> Loosely-scoped mixins has a huge potential of helping us solve the issue of web components reimplementing existing components just for "branded" styling.<br>
&lt;fantasai> ACTION: fantasai to talk to Antti and get back a Webkit position on this.<br>
&lt;fantasai> astearns: Given what I've heard so far, this is kinda necessary for mixins.<br>
&lt;castastrophe> Do we need some more practical use-cases for web components + loosely-scoped mixins?<br>
&lt;fantasai> astearns: Not sure I'd want mixins that weren't addressable in shadow DOM<br>
&lt;fantasai> emilio: Conceptually if you want flexibility of mixins, you can have a stylesheet that defines your mixins, and import that.<br>
&lt;fantasai> emilio: it's the same thing as inheriting into shadow.<br>
&lt;fantasai> emilio: just a quesiton of whether you're doing explicitly or not<br>
&lt;fantasai> astearns: but that's an extra step...<br>
&lt;fantasai> SUMMARY: Desire to have mixins be loosely matched, but concerns about performance and implementation complexity.<br>
</details>


-- 
GitHub Notification of comment by css-meeting-bot
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12671#issuecomment-4172869205 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 1 April 2026 20:46:35 UTC