Search code examples
csssassmedia-queriesmixinsextend

SCSS @extend inside media query alternatives to avoid duplicate properties


In SCSS, you can use @mixin and @extend to reuse code. For example:

%even-row { background: pink; }

@mixin even-rows-mixin($columns) {
    @for $i from 1 through $columns {
        &:nth-child(#{2 * $columns}n + #{$i + $columns}) {
            @extend %even-row;
        }
    }
}

li {
    @include even-rows-mixin(8);
    width: 12%;
}

Will generate:

li:nth-child(16n + 9),
li:nth-child(16n + 10),
li:nth-child(16n + 11),
li:nth-child(16n + 12),
li:nth-child(16n + 13),
li:nth-child(16n + 14),
li:nth-child(16n + 15),
li:nth-child(16n + 16) {
    background: pink;
}

li {
    width: 12%;
}

However, I'd like to use that mixin inside a media query, which is not possible:

@media (max-width: X) {
    li {
        @include even-rows-mixin(8);
        width: 12%;
    }
}

@media (max-width: Y) {
    li {
        @include even-rows-mixin(4);
        width: 16%;
    }
}

This will throw an error:

You may not @extend an outer selector from within @media. You may only 
@extend selectors within the same directive. From "@extend %even-row"
on line N.

I could just remove @extend and inline the properties in the mixin:

@mixin even-rows-mixin($columns) {
    @for $i from 1 through $columns {
        &:nth-child(#{2 * $columns}n + #{$i + $columns}) {
            background: pink;
        }
    }
}

But that will generate duplicated code:

@nth-child(16n + 9)  { background: pink; }
@nth-child(16n + 10) { background: pink; }

...

@nth-child(16n + 16) { background: pink; }

I wonder if there's a better way to write this code without generating duplicated properties or having to write all the possible selectors manually, maybe using interpolation or some other feature I'm not aware of.

This question came to me after answering this other question: How to select even rows with list items, just as a reference if you want to take a look to the whole thing.


Solution

  • You can construct a selector variable with the entire selector group.

    @mixin even-rows-mixin($columns, $rule) {
       $selector : '';
        @for $i from 1 through $columns {
          $selector:  $selector + $rule + '('+ #{2 * $columns}n  + "+" + #{$i + $columns} + ') ,';
        }  
      #{$selector} {
        @content;
      }
    }
    
    li {
        width: 12%;
    }
    @include even-rows-mixin(8, 'li:nth-child') {
     background-color: pink; 
    };
    

    Note: For some reason its not working in codepen editor. But it's working with node-sass