Search code examples
csslessless-mixins

Can I use mixins to generate new mixins in LESS?


I'm trying to use LESS to dynamically generate a set of mixins that would help me write cleaner media query code. So far in my limited knowledge of the language I've put together code that looks like this:

@sizes: xxs, xs, sm, md, lg;

.mediaQueries(@iterator:1) when(@iterator <= length(@sizes)) {

    //Extract name
    @sizeName: extract(@sizes, @iterator);

    //Attempt to build min-width query 
    .MQ-min-@{sizeName} (@content) {
      @media (min-width: @screen-@{sizeName}) {
        @content();
      }
    }

    //Attempt to build max-width query 
    .MQ-max-@{sizeName} (@content) {
      @media (max-width: @screen-@{sizeName}) {
        @content();
      }
    }

    .mediaQueries((@iterator + 1));
}
.mediaQueries();

The goal is to have a set of mixins that would allow me to easily and cleanly define some CSS properties for a specific breakpoint, like so:

.generic-class {
  background: black;
  //Sizes @screen-sm and up 
  .MQ-min-sm({ 
    background: transparent;
  })
}

It doesn't work. Something to note, I'm trying to interpolate the size name into a variable name that would then output me a the px value of that variable into the @media query. Is something like this even possible?

Otherwise my compiler currently breaks on the start of the nested mixin (.MQ-min-@{sizeName} (@content) {) with the error:

Potentially unhandled rejection [2] Missing closing ')' in file ../mixins.less line no. 43

Is something like what I'm trying to achieve possible?


Solution

  • I think the simplest way for you to achieve this is by using a single parametric mixin like given below. This avoids the need for all those iterations, dynamic mixin creations etc.

    @sizes: xxs, xs, sm, md, lg;
    @screen-xxs: 100px;
    @screen-sm: 200px;
    
    .MQ(@content, @sizeName, @max-min) { /* get ruleset, size name and min/max as input */
      @selector: ~"(@{max-min}-width: @{screen-@{sizeName}})"; /* form the media selector */
      @media @selector { /* use it */
        @content();
      }
    }
    
    .generic-class {
      background: black;
      .MQ({ 
            background: transparent;
          }, /* ruleset */
          sm, /* screen size */
          max /* min/max */
      );
    }
    

    If the mixins are for your own usage then this is all that you need. If it is for distribution as library then you may want to put some guards on @sizeName and @max-min variables to restrict invalid values.

    Note: Less compiler always had a problem with the interpolation here - @media (min-width: @screen-@{sizeName}) also (I am not sure if it has been addressed) and that's why I created a temp variable.