core icon indicating copy to clipboard operation
core copied to clipboard

Star selectors leak scoped styles into children

Open thecodewarrior opened this issue 10 months ago • 3 comments

Vue version

3.5.13

Link to minimal reproduction

https://play.vuejs.org/#eNqNUtFOwjAU/ZWmLyQGNwn6gpNEDSb6oEZ8bGLmeoFi1zZtN2YI/+5t5wAjGt52zz09O/fes6bXxiR1BXREM1dYYTxx4CszZkqURltPbnVpyMzqkvSSNBSB3rtkKkvbB0jFwkNpZO4BK0IyLmpSyNy5K0ZXesVohLERBNLfRZbiC/zK0j0dLJ3/lEBcoQ3wSExQjZyMZsI6f1oshORk3aq9a8vBjsjANMRpKTixwNEmIZvoNSiNaZ96VFMzMU+WTiscOz5ntEAvQoJ9Ml5o5RgddcKM5lLq1UPEvK2g3+HFAoqPA/jSNQFj9NmCA1sDo9uez+0cfNueTB+hwe9ts9S8ksj+p/kCOFwVPLa0m0pxtL3Hi27v4/GEmr+6SeNBuW6oYDQuJfIZxWOGM/w1+s7uMDn/XuYGt9gF4UBujs2FUCr47sIQAnAXztplYYtOAS/Gd/CRYUniD9pRTM45LgPTcWYaDMXPSLzVYMOCcJhhcpEMhnTzBXDwBpE=

Steps to reproduce

  • Create a component Foo
    • Add class="foo" on the root element
    • Add a scoped style .foo * { border: 1px solid red; }
  • Create a component Bar
    • Create multiple nested elements (not just a root element)
  • Add <bar/> into Foo's root

What is expected?

The star selector should be scoped to inside Foo

What is actually happening?

The star selector isn't being scoped, which causes the styles to leak into child elements

System Info


Any additional comments?

The example provided includes a :first-child pseudo-class, which makes the behavior (and use case) a bit clearer, however the scoping issues occur regardless of whether the pseudo-class is specified.

We ran into this issue while attempting to upgrade from Vue 2 to Vue 3, where certain selectors that used to be scoped to immediate children were suddenly applying to deep children. The usage sites themselves were flawed in other ways, and should have been using direct child combinators, but those are workarounds and shouldn't be necessary for scoping to apply correctly.

In the past Vue 3 used to mirror Vue 2's behavior, which scopes only the star selector. (Vue 2 demo). That behavior itself isn't ideal, which was reported in #10548 and then addressed in #10551, however in doing so it caused the styles to leak to children instead of from parents. More manageable, but still not ideal.

Here's a sample of different selectors and how they compile in Vue 2 vs Vue 3 ([--] represents the scoping attribute selector)

Source                Vue 2                     Vue 3
:active               [--]:active               [--]:active
*:active              *[--]:active              [--]:active
.foo :first-child     .foo[--] :first-child     .foo[--] :first-child
.foo *                .foo *[--]                .foo[--] *
.foo *:first-child    .foo *[--]:first-child    .foo[--] *:first-child

The solution for this would be to scope both the final class selector and the star selector:

Source                Vue 3 (fixed)
:active               [--]:active
*:active              [--]:active
.foo :first-child     .foo[--] :first-child
.foo *                .foo[--] [--]
.foo *:first-child    .foo[--] [--]:first-child

Ideally the bare pseudo-class (.foo :first-child) would be fully scoped too, but if the goal is parity with Vue 2, that's not necessary.

Also the ticket that started this, #10548, isn't actually running into a fundamental scoping issue caused by star selectors. The same thing applies to any non-direct-child combinators (demo), but in their case the star selector made it difficult to manage. The only way to solve the fundamental scoping issue would be to add the scope attribute to every level of the selector (.foo .bar :first-child -> .foo[--] .bar[--] [--]:first-child), but I'm guessing that has an adverse performance impact, otherwise it likely would have done that from the start.

thecodewarrior avatar Feb 18 '25 21:02 thecodewarrior

Is there a way to revert this behavior?

rkh avatar May 28 '25 14:05 rkh

@thecodewarrior I reverted #12918 because it introduced a breaking change, leading to vuejs/core#13401. For now, we haven't found a perfect solution yet.

@skirtles-code created a discussion https://github.com/vuejs/core/issues/13405 for further discussion of how to handle trailing * selectors.

edison1105 avatar May 29 '25 00:05 edison1105

This seems related to https://github.com/vuejs/core/issues/8868

kleinfreund avatar Jun 02 '25 06:06 kleinfreund