Leon
Leon

Reputation: 2149

SASS nested mixins issue

I'm currently strugging with the following issue (note: the actual mixins are a bit more complex, I've simplified them, but I definately do want to use mixins for this approach)

I have this mixin for some breakpoints:

@mixin sm {
    @media (max-width: 740px) {
        @content;
    }
}
@mixin lg {
    @media (min-width: 741px) {
        @content;
    }
}

And I have this mixin to apply margins to an element (the actual mixin is a bit more complex)

@mixin margin {
    @include sm {
        margin: 10px;
    }
    @include lg {
        margin: 20px;
    }
}

So basically, I use the mixin on an element, and based on the viewport size, the element gets a different margin. The problem is this: If I want to use the margin mixin for an element, but only on a large viewport size, I would get something like this:

.foo {
    @include lg {
        @include margin;
    }
}

The output now becomes:

@media (min-width: 741px) and (max-width: 740px) {
  .foo {
      margin: 10px; 
    } 
}   
@media (min-width: 741px) and (min-width: 741px) {
  .foo {
        margin: 20px; 
    } 
}

Which makes sense, but I'd like to have an output like this:

@media (min-width: 741px) {
  .foo {
        margin: 20px; 
    } 
}

Ideally I would not include the breakpoint mixin in the margin mixin, but I couldn't think of any way to do it otherwise. Does anyone know if this is possible?

Thanks!

Upvotes: 1

Views: 1100

Answers (1)

Marvin
Marvin

Reputation: 10152

One easy to use approach I could think of is the one where you extend your margin mixin by conditionals and optional parameters to be able to exclude specific media queries if necessary.

@mixin margin($sm: true, $lg: true) {
    @if $sm {
      @include sm {
          margin: 10px;
      }
    }
    @if $lg {
      @include lg {
          margin: 20px;
      }
    }
}

.margin {
  @include margin;
}

.margin-only-on-lg {
  @include margin(false, true);
}

Using the mixin is still simple and the use of media queries is only necessary in the mixin itself. It is also pretty robust, so that in case a developer is not aware of the optional parameters he can do the following without breaking the logic.

.foo {
    @include lg {
        @include margin;
    }
}

But of course it outputs some unnecessary CSS. If you see a problem here and/or if you want to have a more flexible approach of passing different concrete margin values as parameters, the following modification in combination with warnings for missing optional parameters at compile time (see my answer there) might be a better approach:

@mixin margin($sm: false, $lg: false) {
    @include sm {
      margin: warn-on-false("margin()", $sm, 10px);
    }
    @include lg {
      margin: warn-on-false("margin()", $sm, 20px);;
    }
}

.margin {
  @include margin(null, null); // I'm aware of the parameters, but use the default values
}

.margin-only-on-lg {
  @include margin(0, null); // set margin to 0 on small screen size; use default for lg 
}

This way the above (undesired) way of defining the .foo class due to a lack of knowing the optional parameters results in a warning at compile time and in addition the developer can use different margin values in special cases. Of course, it would also be possible to simply go with the boolean parameters and make them mandatory.

Upvotes: 1

Related Questions