title: Less - parametric mixin with shorthand numeric and or string values, with rem and px fallback
body: <p>Using Less, I'm trying output css combination shorthand values for properties, whilst detecting the type of a value passed as a variable depending on how many values are passed as variables.</p>
  
  <ul>
  <li>one value <code>@v1</code></li>
  <li>two values <code>@v1</code> and <code>@v2</code></li>
  <li>four values <code>@v1</code>, <code>@v2</code>, <code>@v3</code> and <code>@v4</code></li>
  </ul>
  
  <p>And for each variable detected, check if is is a number greater than 0, and if so output a rem and px value for each property value, so the complied css might be something like:</p>
  
  <pre><code>.demobox{
    border-width: 10px 20px 0 0;
    border-width: 1rem 2rem 0 0;
    padding: 0 50px;
    padding: 0 2rem;
    margin: 20px auto;
    margin: 2rem auto;
    font-size: 16px;
    font-size: 1.6rem;
  }
  </code></pre>
  
  <p>This is fairly easy to solve where no more than two values are ever declared, but If I need four values, the number of nested mixins and guards becomes pretty complicated.</p>
  
  <p><em><strong>The reason for opting for rems is the ease of implementation compared to ems, and prevents having the headache of remembering to have to reset the font-size to every parent item. Additionally the older device support issues are comparable, for both rems and ems, so I figure a px fallback works best.</strong></em></p>
  
  <p>Here's' the <a href="http://codepen.io/onebitrocket/pen/hwqCi" rel="nofollow">code pen</a> of where I am so far, but I'll try and explain my workings so far as best as I can.</p>
  
  <p>Is there a way to simplify this process? I'm new to less. I imagine there is a much simpler way to detect </p>
  
  <ul>
  <li>the number of declared values</li>
  <li>the type of each value</li>
  <li>if the number value requires a rem and px value fallback</li>
  </ul>
  
  <p>Thanks</p>
  
  <p>I have a parametric mixin which outputs a <code>rem</code> value and <code>px</code> fallback value along with the declared property</p>
  
  <pre><code>@root-font-size: 10;
  
  .rem(@property, @v1) when (@v1 = @v1){
      @px1: @v1 * @root-font-size;
      @{property}:  ~'@{px1}px';
      @{property}: ~'@{v1}rem';
  }
  </code></pre>
  
  <p>This is great for quickly generating both the rem and pixel fallback for a set css property</p>
  
  <pre><code>/* call the rem mixin */
  .demobox {
      .rem(margin, 2);
  }
  /* output */
  .demobox {
      margin: 20px;
      margin: 2rem;
  }
  </code></pre>
  
  <p>One really useful case for this is generating <code>font-size</code> and <code>line-height</code> values with fallbacks</p>
  
  <pre><code>/* call the font-classes mixin */
  .font-classes(@fontsize, @lineheight) {
      .rem(font-size, @fontsize);
      .rem(line-height, @lineheight);
  }
  /* output */
  .demobox {
      font-size: 16px;
      font-size: 1.6rem;
      line-height: 24px;
      line-height: 2.4rem;
  }
  </code></pre>
  
  <p>So far so good, i can output a rem and pixel fallback for a single value and nest the <code>.rem</code> mixin with in another mixin. </p>
  
  <p>At the moment, if I want to pass more than one value I have to call the <code>.rem</code> mixin for each required value:</p>
  
  <pre><code>/* call the rem mixin */
  .demobox {
      .rem(margin-left, 2);
      .rem(margin-right, 2);
  }
  /* output */
  .demobox {
      margin-left: 20px;
      margin-left: 2rem;
      margin-right: 20px;
      margin-right: 2rem;
  }
  </code></pre>
  
  <p>Ideally I'd like to be able to do is pass either one two or four values with a property to the<code>.rem</code> mixin. </p>
  
  <pre><code>.demobox {
      .rem(margin, 2, 1);
  }
  </code></pre>
  
  <p>As I mentioned earier this is not so difficult for two values, and to check what the value type is. This requires a version of the <code>.rem</code> mixin with a gaurd applied to check that <code>@v2</code> is declared.</p>
  
  <p>This the triggers a nested <code>.rem-two</code> mixin.</p>
  
  <pre><code>// when there are two values
  .rem(@property, @v1, @v2) when (@v1 = @v1) and (@v2 = @v2){
      .rem-two(@property, @v1, @v2);
  }
  </code></pre>
  
  <p>There are three versions of the <code>.rem-two</code>, where the guards are different for each.</p>
  
  <p>Detect if both <code>@v1</code> and <code>@v2</code> are numbers greater than 0</p>
  
  <pre><code>.rem-two(@property, @v1, @v2) when (@v1 = @v1) and not (@v1 = 0) and (isnumber(@v1)) and (@v2 = @v2) and not (@v2 = 0) and (isnumber(@v2)) {
      @px1: @v1 * @root-font-size;
      @px2: @v2 * @root-font-size;
      @{property}:  ~'@{px1}px @{px2}px';
      @{property}: ~'@{v1}rem @{v2}rem';
  }
  </code></pre>
  
  <p>Detect if both <code>@v1</code> and <code>@v2</code> are numbers greater than 0</p>
  
  <pre><code>.rem-two(@property, @v1, @v2) when (@v1 = @v1) and not (@v1 = 0) and (isnumber(@v1)) and (@v2 = 0), not (isnumber(@v2)){
      @px1: @v1 * @root-font-size;
      @{property}:  ~'@{px1}px @{v2}';
      @{property}: ~'@{v2}rem @{v2}';
  }
  
  /* call the rem mixin */
  .demobox {
      .rem(margin, 2, 1);
  }
  /* outputs */
  .demobox {
      margin: 10px 20px;
      margin: 1rem 2rem;
  }
  </code></pre>
  
  <p>Detect if both <code>@v1</code> is a number greater than 0 and <code>@v2</code> is either a value of 0 or not a number</p>
  
  <p><em>the '@px2' pixel fallback is not required, so is removed.</em></p>
  
  <pre><code>.rem-two(@property, @v1, @v2) when (@v1 = @v1) and not (@v1 = 0) and (isnumber(@v1)) and (@v2 = 0), not (isnumber(@v2)){
      @px1: @v1 * @root-font-size;
      @{property}:  ~'@{px1}px @{v2}';
      @{property}: ~'@{v2}rem @{v2}';
  }
  /* call the rem mixin */
  .demobox {
      .rem(margin, 2, auto);
  }
  /* outputs */
  .demobox {
      margin: 10px auto;
      margin: 1rem auto;
  }
  </code></pre>
  
  <p>Detect if both <code>@v1</code> is either a value of 0 or not a number and <code>@v2</code> is a number greater than 0</p>
  
  <p><em>the '@px1' pixel fallback is not required, so is removed.</em></p>
  
  <pre><code>.rem-two(@property, @v1, @v2) when (@v1 = 0), not (isnumber(@v1)) and (@v2 = @v2) and not (@v2 = 0) and (isnumber(@v2)){
      @px2: @v2 * @root-font-size;
      @{property}:  ~'@{v1} @{px2}px';
      @{property}: ~'@{v1} @{v2}rem';
  }
  /* call the rem mixin */
  .demobox {
      .rem(margin, 0, 20);
  }
  /* outputs */
  .demobox {
      margin: 0 20px;
      margin: 0 2rem;
  }
  </code></pre>
  
  <p>As there are only 3 possibilities for this two value version of the mixin  It is easy to solve this problem, but with 3 or four values the number of nested mixins and guards expands to cover all possibilities.</p>
  
  <p>As additional examples. 
  I have repurposed the <code>.rem</code> mixin to output <code>px</code> and <code>rem</code>, <code>border-radius</code> values with vendor prefixing, to ensure that it only processes numeric values, I have guards to check if the value being passed is a number that is greater that 0</p>
  
  <pre><code>.prefix(@property, @v1) when (isnumber(@v1)) and (@v1 &gt; 0) {
      @px1: @v1 * @root-font-size;
      -webkit-@{property}: ~'@{px1}px';
      -moz-@{property}: ~'@{px1}px';
      @{property}: ~'@{px1}px';
      -webkit-@{property}: ~'@{v1}rem';
      -moz-@{property}: ~'@{v1}rem';
      @{property}: ~'@{v1}rem';
  }
  /* call prefix mixin */
  .demobox{
      .prefix(border-radius,5);
  }
  /* output */
  .demobox{
      -webkit-border-radius: 50px;
      -moz-border-radius: 50px;
      border-radius: 50px;
      -webkit-border-radius: 5rem;
      -moz-border-radius: 5rem;
  }
  </code></pre>
  
  <p>I can swap the guards around to check that the value is either 0 (so i can reset a property) or if it is anything else than a number, such as outputting prefixed box sizing.</p>
  
  <p>The reason I ask a guard of <code>not (isnumber(@v1))</code>, rather than <code>(isstring(@v1))</code>, is so I don't have to add single quotes the the value `'border-box'.</p>
  
  <pre><code>.prefix(@property, @v1) when (@v1 = 0), not (isnumber(@v1)) {
      -webkit-@{property}: ~'@{v1}';
      -moz-@{property}: ~'@{v1}';
      @{property}: ~'@{v1}';
  }
  /* call prefix mixin */
  .demobox {
      .prefix(box-sizing, border-box);
  }
  /* output */
  .demobox {
      -webkit-box-sizing: border-box;
      -moz-box-sizing: border-box;
      box-sizing: border-box;
  }
  </code></pre>