Skip to content

@vue/compiler-sfc: Partly unscoped styling with selector list & :deep & nesting #15205

Description

@stellar7073

Version

3.6.0-rc.2

Reproduction link

play.vuejs.org/

Steps to reproduce

When a <style scoped> rule has (1) a comma-separated selector list, where (2) one member uses :deep(), and (3) the rule contains nested child rules, the scope id ([data-v-xxx]) is not injected into the non-:deep() selectors in the list. Those selectors become unscoped and leak globally.

An example which triggers this bug:

.a,
.b :deep(.c) {
  color: red;
  > span { color: blue; }
}

What is expected?

.a[data-v-7ba5bd90],
.b[data-v-7ba5bd90] .c {
& {
  color: red;
}
> span { color: blue;
}
}

What is actually happening?

.a is unscoped.

.a,
.b[data-v-7ba5bd90] .c {
& {
  color: red;
}
> span { color: blue;
}
}

According to Claude, the problem seems to be caused as follows. Note that I did not verify this.

Root cause appears to be in rewriteSelector (packages/compiler-sfc/src/style/pluginScoped.ts): rule.__deep is tracked per-rule but set as a side effect while rewriting whichever member contains :deep(). When a nested rule is detected, the code sets shouldInject = rule.__deep and defers injection to a generated & { ... } wrapper. Because selectors are processed in source order, a plain selector processed before the :deep() member sees rule.__deep still unset (→ skips injection on itself), while the later & wrapper sees rule.__deep === true (→ also skips injection), so the scope id lands nowhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🔨 p3-minor-bugPriority 3: this fixes a bug, but is an edge case that only affects very specific usage.has workaroundA workaround has been found to avoid the problemscope: sfc

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions