sassvuejs2vitenode-sass

converting from node-sass to sass - alternative to using a parent selector in a mixin


I am trying to upgrade my Vue stack to support vite because it generates a much faster codebase for use on the web. I have done this in several projects, but not on any that use sass. Since vite uses sass instead of node-sass, I have to migrate all my sass code as well. I am using a sass-heavy 3rd-party element toolkit and it is throwing the following error:

[plugin:vite:css] [sass] Top-level selectors may not contain the parent selector "&".
   ╷
71 │     &.el-select-dropdown.is-multiple
   │     ^
   ╵

the block says:

@mixin select($type, $color) {
  .select-#{$type}.el-select .el-input  {
    .el-input__suffix{
      display: flex;
      align-items: center;
    }
  }
  /* more code goes here */
  &.el-select-dropdown.is-multiple
  .el-select-dropdown__item.selected{
    &.select-#{$type}{
      color: $color;
    }
  }
}

@include select('default', $default-color);
@include select('info', $info-color);

How would I refactor this to be compatible with the sass standard? Or, why/how is this working with node-sass?


Solution

  • Let's have a look at the mixin:

    @mixin select($type, $color) {
      .select-#{$type}.el-select .el-input  {
        .el-input__suffix{
          display: flex;
          align-items: center;
        }
      }
      /* more code goes here */
      &.el-select-dropdown.is-multiple
      .el-select-dropdown__item.selected{
        &.select-#{$type}{
          color: $color;
        }
      }
    }
    

    It's made of two blocks

    First block:

      .select-#{$type}.el-select .el-input  {
        .el-input__suffix{
          display: flex;
          align-items: center;
        }
      }
    

    This one is easy, when you do @include select('default', $default-color); it will simply define the selector as: .select-default.el-select .el-input:

    .select-default.el-select .el-input  {
        .el-input__suffix{
          display: flex;
          align-items: center;
        }
    }
    

    It will compile to:

    .select-default.el-select .el-input .el-input__suffix {
      display: flex;
      align-items: center;
    }
    

    Second block:

      &.el-select-dropdown.is-multiple
      .el-select-dropdown__item.selected{
        &.select-#{$type}{
          color: $color;
        }
      }
    

    It's using the &, this is the parent selector in sass:

    The parent selector, &, is a special selector invented by Sass that’s used in nested selectors to refer to the outer selector. It makes it possible to re-use the outer selector in more complex ways, like adding a pseudo-class or adding a selector before the parent. Parent Selector - Sass lang

    It will be replaced by the parent selector, if you are trying to use the mixin at the root level, you don't have a parent selector, that's why it's failing.

    If you use it within a selector (parent selector):

    .foobar {
      @include select('default', $default-color);
    }
    

    The & will be replaced by .foobar.

      .foobar.el-select-dropdown.is-multiple
      .el-select-dropdown__item.selected{
        &.select-default{
          color: $default-color;
        }
      }
    

    Conclusion

    The mixin is perfectly fine, you just need to use it inside a parent selector.