Universal selectors in scoped CSS #13405
Replies: 3 comments 4 replies
-
I also don't consider #10548 a bug. Here is a scenario that #10551 did not fix. see demo. I believe we should revert PR #10551 to maintain behavior consistent with Vue 2. After reverting #10551
.foo * {
color: red;
}
.foo {
* {
color: red;
}
}
.foo {
& * {
color: red;
}
} will be compiled to .foo *[data-v-7ba5bd90] {
color: red;
}
.foo {
*[data-v-7ba5bd90] {
color: red;
}
}
.foo {
& *[data-v-7ba5bd90] {
color: red;
}
} If #10548 still needs to fix, possible solutions include:
|
Beta Was this translation helpful? Give feedback.
-
IMO #10548 isn't a bug. Or rather, changing the If a "fix" beyond using classes or a custom id approach is still needed, maybe a counter part to :shallow(*) {
color: red;
} Either way, introducing this change in a minor release without any deprecation warning was a bit of a shock, tbh. Glad I pushed working on a fix on our side in frustration, and hadn't really begun by the time the change got reverted. |
Beta Was this translation helpful? Give feedback.
-
I think we should revert to always applying the attribute selector to subject selectors. While this approach has its limitations, it's more consistent and predictable. To improve correctness, I suggest introducing a new compiler option,
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
TLDR: We currently have special handling for selectors containing
*
, but I don't know why and I think they should be treated like any other selector.I've tried to trace the history of how we got to where we are and document it for further discussion of how to proceed.
Introduction
Consider this selector:
This will be compiled to something like:
The
span
is the subject of the selector, that's the element that will receive the styles, so we add an extra attribute to ensure it is scoped to the current component.But we don't add any scoping to the
div
, and it's been this way for a long time (possibly forever). Ancestors in the selector are not scoped, they don't need to be in the current component.Now consider this selector:
In Vue 3.5.14, this compiled to:
This was changed by #12918, so in 3.5.15 we instead get:
While adding the scoping attribute to the subject does seem like a step in the right direction, I think it should have also been removed from the ancestor, to make it consistent with how we handle
div span
. I would expect:Or, equivalently,
div *[data-v-111]
.This is how it worked in Vue 2. It's also how it worked in Vue 3 prior to 3.4.22. It was changed by #10551, which was a fix for #10548.
However, I don't think #10548 was actually a bug.
The reproduction in #10548 uses a selector of
.comp *
and notes that the.comp
part isn't scoped to the current component, so if an ancestor usesclass="comp"
it will leak into the current component. But that's true for other selectors too, not just*
selectors. If you use.comp div
then you'll see the same thing. It's a consequence of only scoping the subject of the selector and not the ancestors. There's nothing special about*
here, yet the fix in #10551 singled out*
selectors for special treatment.Perhaps I'm missing something, but this just seems like a misunderstanding.
Specificity
By consistently adding a single attribute to the subject of the selector, we ensure that the specificity of scoped rules, relative to each other, remains the same. The specificity is increased, but always by the same amount.
The change in 3.5.15 increased the specificity for selectors like
div *
, relative to other scoped selectors. This had led to breakages, e.g. #13401 and #13402.Nesting
Consider these 3 rules:
Assuming we're in an environment that supports nesting, these should all be considered equivalent. Tools like PostCSS should be free to treat these as interchangeable and rewrite them as needed. Currently, this is not the case. The second example works as I would expect, replacing the
*
with[data-v-111]
, but the others also scope the.foo
.:deep()
:deep()
can be used to move the scoping to an earlier part of the selector.If someone wants a selector of
div[data-v-111] *
(as generated bydiv *
in 3.4.22 to 3.5.14), they can achieve that usingdiv :deep(*)
instead.Beta Was this translation helpful? Give feedback.
All reactions