Search code examples
vue.jssassdart-sassvue-sfc

:deep() syntax with nested scss rules


I am trying to migrate some Vue.js Single File Components from ::v-deep syntax to :deep(), as described here. However, I am not sure how to make it work with nested SCSS rules with &__*. Rules without &__* work just fine.

The SCSS compiler we use is dart-sass.

Example

For example, having this original snippet:

::v-deep .wrapper {
    display: flex;

    &__element {
        display: block
    }
}

Correctly compiles the code to

[data-v-S0m3Ha5h] .wrapper__element {
    display: block;
}

And throws a warning: [@vue/compiler-sfc] ::v-deep usage as a combinator has been deprecated. Use :deep(<inner-selector>) instead.

:deep() in top-level rule

I have tried converting it to :deep() like this:

:deep(.wrapper) {
    display: flex;

    &__element {
        display: block
    }
}

which results in a compiler error, because :deep(wrapper)__element is not a valid selector.

:deep() in the nested rule

So I moved the :deep to the nested rule:

.wrapper {
    display: flex;

    :deep(&__element) {
        display: block
    }
}

which compiles without errors, but produces botched css:

.wrapper[data-v-S0m3Ha5h] &__element {/* ... */}

Question

How can I use nested &__* rules with :deep()?


Solution

  • Sass apparently doesn't allow the selector argument list of :deep(or of any other pseudo-class) to be nested because of the reason you mentioned, but there are a couple workarounds.

    Workaround 1: Split up the Sass styles

    Split up the styles so that :deep's selector list is not nested:

    <!-- MyParent.vue -->
    <style scoped lang="scss">
    :deep(.wrapper) {
        display: flex;
    }
    :deep(.wrapper__element) {
        display: block;
    }
    </style>
    

    demo 1

    Workaround 2: Split up the class names

    Break up the BEM class names (from wrapper__element to __element) so that parent selectors are not necessary:

    <!-- MyComponent.vue -->
    <template>
        <div class="wrapper">
            <div class="__element">...</div>
        </div>
    </template>
    

    or augment wrapper__element with an additional __element class (if you can ignore the repetition):

    <!-- MyComponent.vue -->
    <template>
        <div class="wrapper">
            <div class="wrapper__element __element">...</div>
        </div>
    </template>
    

    For some reason, this scenario requires :deep to based on a selector (e.g., root in the example below):

    <!-- MyParent.vue -->
    <template>
        <div class="root">
            <MyComponent />
        </div>
    </template>
    
    <style scoped lang="scss">
    .root:deep(.wrapper) {
        display: flex;
    
        .__element {
            display: block;
        }
    }
    </style>
    
    <!-- MyComponent.vue -->
    <template>
      <div class="wrapper">
        <h1>.wrapper</h1>
    
        <!-- Add __element class here 👇 (and optionally remove wrapper__element) -->
        <div class="wrapper__element __element">
          <h1>.wrapper__element</h1>
        </div>
      </div>
    </template>
    

    demo 2