Search code examples
csslessmixinsless-mixins

Mixin guards in Less based on variables


I'm trying to create a button mixin in less that is highly customizable. For example, I want a client to be able to go in their .less file and write:

.my-button {
    .btn(@bg: #FFF, @font: "", @color: #000, @size: 'large'...);
    ...
}

Now in my .btn(...) mixin, I'm trying to figure out how to correctly incorporate Less mixin guards to do what I want.

I'm guessing I can write something like this:

.btn(@bg: #FFF, @font: "", @color: #000, @size: 'large'...) {
    // All of my styling here
}

.btn(@bg: #FFF, @font: "", @color: #000, @size: 'large'...) when (@size = 'medium') {
    // All of my styling here BUT with new edits for 'medium' sizing
}

but this seems terribly inefficient and redundant to me. Is there a better way to allow for a really customizable mixin, but without making the client make multiple mixin calls? I really want to be able to do this with just calling .btn(...)


Solution

  • Yes, structuring the mixins like that provided in question is a bit inefficient because of common styles having to be repeated across multiple mixins.

    There are a few ways to avoid the repetition and they are provided below.

    Method 1 (Using Guards):

    As you can see here, the common properties are specified only once within the parent mixin and the properties that are dependent on the size alone are provided within the guards. The con of this method is that when the size has a value outside of the list of supported values the mixin outputs nothing from the guarded area and does not throw any warnings also. This is because Less compiler silently skips the guarded area when no match is found and can be problematic when the end user knows nothing about the Less code.

    .my-button-large {
        .btn(@font: "Arial");
    }
    .my-button-medium {
        .btn(@bg: #000, @font: "Calibri", @size: "medium");
    }
    .my-button-small {
        .btn(@bg: #777, @font: "Verdana", @size: "small");
    }
    
    .btn(@bg: #FFF, @font: "", @color: #000, @size: "large"){
      background-color: @bg;
      font-family: @font;
      color: @color;
      & when (@size = "large"){
        font-size: 24px;
        padding: 12px;
        line-height: 24px;
      }
      & when (@size = "medium"){
        font-size: 18px;
        padding: 8px;
        line-height: 18px;    
      }
      & when (@size = "small"){
        font-size: 12px;
        padding: 4px;
        line-height: 12px;    
      }  
    }
    

    Method 2 (Using Pattern Matching):

    This is an alternate method where an error would be thrown during compilation if the value provided by the user is outside of the list of allowed values. This is because we are making use of the pattern matching to act as a switch.

    .my-button-large {
        .btn(@font: "Arial");
    }
    .my-button-medium {
        .btn(@bg: #000, @font: "Calibri", @size: "medium");
    }
    .my-button-small {
        .btn(@bg: #777, @font: "Verdana", @size: "small");
    }
    
    .btn(@bg: #FFF, @font: "", @color: #000, @size: "large"){
      background-color: @bg;
      font-family: @font;
      color: @color; 
      .bg-size(@size);
    }
    .bg-size("large"){
      font-size: 24px;
      padding: 12px;
      line-height: 24px;
    }
    .bg-size("medium"){
      font-size: 18px;
      padding: 8px;
      line-height: 18px;    
    }
    .bg-size("small"){
      font-size: 12px;
      padding: 4px;
      line-height: 12px;    
    }