Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
1 contributor

Users who have contributed to this file

  • Start Date: 2020-01-21
  • Target Major Version: 2.x, 3.x
  • Reference Issues: N/A
  • Implementation PR: N/A

Summary

Provide more consistent custom CSS extensions in Single File Component scoped styles.

Basic example

<style scoped>
/* deep selectors */
::v-deep(.foo) {}
/* shorthand */
:deep(.foo) {}

/* targeting slot content */
::v-slotted(.foo) {}
/* shorthand */
:slotted(.foo) {}

/* one-off global rule */
::v-global(.foo) {}
/* shorthand */
:global(.foo) {}
</style>

Motivation

Vue SFC scoped styles makes the CSS apply to the current component only. There are a number of cases that users commonly run into that can be improved.

Deep Selectors

Sometimes we may want to explicitly make a rule target child components.

Originally we supported the >>> combinator to make the selector "deep". However, some CSS pre-processors such as SASS has issues parsing it since this is not an official CSS combinator.

We later switched to /deep/, which was once an actual proposed addition to CSS (and even natively shipped in Chrome), but later dropped. This caused confusion for some users, since they worry that using /deep/ in Vue SFCs would make their code not supported in browsers that have dropped the feature. However, just like >>>, /deep/ is only used as a compile-time hint by Vue's SFC compiler to rewrite the selector, and is removed in the final CSS.

To avoid the confusion of the dropped /deep/ combinator, we introduced yet another custom combinator, ::v-deep, this time being more explicit that this is a Vue-specific extension, and using the pseudo-element syntax so that any pre-processor should be able to parse it.

The previous versions of the deep combinator are still supported for compatibility reasons in the current Vue 2 SFC compiler, which again, can be confusing to users. In v3, we are deprecating the support for >>> and /deep/.

As we were working on the new SFC compiler for v3, we noticed that CSS pseudo elements are in fact semantically NOT combinators. It is more consistent with idiomatic CSS for pseudo elements to accept arguments instead, so we are also making ::v-deep() work that way. If you don't care about the explicit v- prefix, you can also use the shorter :deep() variant, which works exactly the same.

The current usage of ::v-deep as a combinator is still supported, however it is considered deprecated and will raise a warning.

Targeting / Avoiding Slot Content

Currently, slot content passed in from the parent are affected by both the parent's scoped styles AND the child's scoped styles. There is no way to author rules that explicitly target slot content only, or ones that do not affect slot content.

In v3, we intend to make child scoped styles NOT affecting slot content by default. To explicitly target slot content, the ::v-slotted() (shorthand: :slotted()) pseudo element can be used.

One-Off Global Rules

Currently to add a global CSS rule we need to use a separate unscoped <style> block. We are introducing a new ::v-global() (shorthand: :global()) pseudo element for one-off global rules.


We are also aware that many users want to be able to use component props or state in the CSS of single-file components. We do have plans to support that but it will be in a separate RFC.

Detailed design

  • >>> and /deep/ support are deprecated.

  • ::v-deep usage as a combinator is deprecated:

    /* DEPRECATED */
    ::v-deep .bar {}

    Instead, use it as a pseudo element that accepts another selector as argument:

    ::v-deep(.bar) {}

    The above compiles to:

    [v-data-xxxxxxx] .bar {}
  • Slot content passed in from the parent no longer gets affected by child scoped styles by default. Instead, the child now needs to use the new ::v-slotted() pseudo element to specifically target slot content:

    ::v-slotted(.foo) {}

    Compiles to:

    .foo[v-data-xxxxxxx-s] {}

    Notice the -s postfix which makes it target slot content only.

  • New pseudo element ::v-global() can be used to apply global rules inside a <style scoped> block:

    ::v-global(.foo) {}

    Compiles to:

    .foo {}
  • The test cases of @vue/compiler-sfc can be used as a reference for the compilation transform details.

Adoption strategy

All previous usage of deep selectors can be supported with deprecation warnings. We will remove the support in a future release when most users have completed the migration.

The slot content behavior change should technically lead to more decoupled styling between parent and child components, however the behavior can indeed break existing styles that rely on child styles applied to slot content. We may need to provide an option to preserve the old behavior.

Unresolved questions

We are not sure about how much the slot styling behavior change would affect existing components - any feedback would be appreciated.