sasslessless-mixinsscss-mixins

converting less mixins to scss


I was using the following LESS mixins to generate css for bidi usage.

.ltr(@ltrRules) {
  body[dir="ltr"] & ,
  body[dir="rtl"] *[dir="ltr"] & , 
  body[dir="rtl"] *[dir="ltr"]&    { @ltrRules(); }
}

.rtl (@rtlRules) {
  body[dir="rtl"] & , 
  body[dir="ltr"] *[dir="rtl"] & , 
  body[dir="ltr"] *[dir="rtl"]&    { @rtlRules(); }
}

.bidi(@ltrRules,@rtlRules) {
  .ltr(@ltrRules);
  .rtl(@rtlRules);
}

those would be later used like this:

// padding
// ------------------------------------------
.padding-start(@p) { 
  .bidi(
   { padding-left: @p } ,
   { padding-right: @p }
  )
}

.padding-end(@p) { 
  .bidi(
   { padding-right: @p } ,
   { padding-left: @p }
  )
}

this way when I eventually wrote:

div.myclass { 
   .padding-start(10px) 
}

I would get a set of rules that would style div.myclass with padding-left:10px if the div was in a left-to-right context or with with padding-right:10px if the div was in a right-to-left context.

I am now trying to convert this to SCSS and I have got to the following:

@mixin ltr {
  body[dir="ltr"] & ,
//body[dir="rtl"] *[dir="ltr"]&      
  body[dir="rtl"] *[dir="ltr"] & { @content }
}


@mixin rtl {
  body[dir="rtl"] & , 
//body[dir="ltr"] *[dir="rtl"]&    
  body[dir="ltr"] *[dir="rtl"] & { @content}
}


/*
@mixin bidi($ltrRules,$rtlRules) {
  @include ltr(@include ltrRules);
  @include rtl(@include rtlRules);
}
*/

@mixin bidi-padding-start($p) { 
   @include ltr { 
    padding-left: $p; 
   } 
   @include rtl { 
    padding-right: $p; 
   }
}

but I still have several issues:

1) If I uncomment the middle selectors in the rtl and ltr mixins I then get the following error: Invalid CSS after "[dir="ltr"]": expected "{", was "&". "&" may only be used at the beginning of a compound selector. I can live without this rule but I would prefer not to... I would expect it to compile

.myclass {
   @include padding-start(10px)
}

into:

body[dir="rtl"] .myclass , 
      body[dir="ltr"] *[dir="rtl"] .myclass , 
      body[dir="ltr"] *[dir="rtl"].myclass  

2) I can't find a way to implement @mixin bidi because I cant find a way to pass along two content blocks to a mixin.

can someone help with this migration please


Solution

  • Currently SASS's functionality is limited to passing only a single @content block into a @mixin. There was a feature request discussion on a github issue but it was closed, and it looks like it never came to fruition.

    In that discussion, someone suggested a workaround that effectively works with @extend. It works by using %placeholders to allow you to reference temporary rulesets. However, it seems a bit hacky and will need to be tested for more complicated rulesets with deeper nesting.

    @mixin ltr {
      body[dir="ltr"] & ,
    //body[dir="rtl"] *[dir="ltr"]&      
      body[dir="rtl"] *[dir="ltr"] & { @content }
    }
    
    
    @mixin rtl {
      body[dir="rtl"] & , 
    //body[dir="ltr"] *[dir="rtl"]&    
      body[dir="ltr"] *[dir="rtl"] & { @content}
    }
    
    @mixin bidi {
      @content;
      @include ltr {
        @extend %ltr !optional;
      }
    
      @include rtl {
        @extend %rtl !optional;
      }
    }
    
    @mixin padding-end($p) {
      @include bidi {
        @at-root { 
          %ltr {
            padding-right: $p;
          }
          %rtl {
            padding-left: $p;
          }
        }
      }
    }
    
    .test {
      @include padding-end(5px);
    }
    

    Another option that may be more readable for future devs may simply be to explicitly call both mixins in place of bidi. It may be possible to write a script to make this migration.

    @mixin padding-end($p) { 
        @include rtl {
            padding-left: $p;
        }
    
        @include ltr {
            padding-right: $p;
        }
    }