Meek Meek - 4 months ago 26
Less Question

Bootstrap convert spacing mixin from sass to less

I have a site running Bootstrap 3.3.7. I use less to adjust the styling. In version 4 of Bootstrap sass is introduced instead of less, and I noticed a new mixin which adds the ability to easily use predefined paddings and margins:

// Width

.w-100 { width: 100% !important; }

// Margin and Padding

.m-x-auto {
margin-right: auto !important;
margin-left: auto !important;
}

@each $prop, $abbrev in (margin: m, padding: p) {
@each $size, $lengths in $spacers {
$length-x: map-get($lengths, x);
$length-y: map-get($lengths, y);

.#{$abbrev}-a-#{$size} { #{$prop}: $length-y $length-x !important; } // a = All sides
.#{$abbrev}-t-#{$size} { #{$prop}-top: $length-y !important; }
.#{$abbrev}-r-#{$size} { #{$prop}-right: $length-x !important; }
.#{$abbrev}-b-#{$size} { #{$prop}-bottom: $length-y !important; }
.#{$abbrev}-l-#{$size} { #{$prop}-left: $length-x !important; }

// Axes
.#{$abbrev}-x-#{$size} {
#{$prop}-right: $length-x !important;
#{$prop}-left: $length-x !important;
}
.#{$abbrev}-y-#{$size} {
#{$prop}-top: $length-y !important;
#{$prop}-bottom: $length-y !important;
}
}
}

// Positioning

.pos-f-t {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: $zindex-navbar-fixed;
}


Source at GitHub

I would like to convert this mixin to less, and use it in my own Bootstrap 3.3.7 project. How would this mixin look like in less?

Answer

Less does not have any @each function or map like Sass does but even then converting this Sass code into its Less equivalent is fairly easy. All that is needed are a couple of loops each of which will mimic the two @each function in Sass and associative arrays.

In Less, we can use both comma and space as delimiters for values. So by using both of them we can achieve a behavior similar to that of maps. Even multi-level maps can be mimicked using this.

(Note: You need to know the basics of Less loops to understand this code but since you say you've already used Less, I assume that you are familar with the concepts).

@props: margin m, padding p; /* the property and abbreviation */
@spacers: xs 10 20, md 20 30; /* the sizes, its length-x and length-y */

.loop-props(@prop-index) when (@prop-index > 0){ /* outer each loop */

  @prop: extract(@props, @prop-index); /* get each prop-abbrev pair based on loop index */
  @prop-name: extract(@prop, 1); /* the first value in each pair is the prop name */
  @abbrev: extract(@prop, 2); /* the second value in each pair is the prop's abbrev */

  /* call size loop mixin with each property name + abbreviation */
  .loop-sizes(@prop-name; @abbrev; length(@spacers)); 

  .loop-props(@prop-index - 1); /* call the next iteration of the outer each loop */
}
.loop-props(length(@props)) !important; /* initial mixin/loop call */

.loop-sizes(@prop-name; @abbrev; @size-index) when (@size-index > 0){ /* inner each */

  @spacer: extract(@spacers, @size-index); /* extract each spacer value based on index */
  @size: extract(@spacer, 1); /* first value in each spacer is the size */
  @x: extract(@spacer, 2); /* second value is the length in X axis */
  @y: extract(@spacer, 3); /* third value is the length in Y axis */

  /* create the selectors and properties using interpolation */
  .@{abbrev}-a-@{size} { 
    @{prop-name}: @y @x; 
  }
  .@{abbrev}-t-@{size} { 
    @{prop-name}-top: @y; 
  }
  .@{abbrev}-r-@{size} { 
    @{prop-name}-right: @x; 
  }
  .@{abbrev}-b-@{size} { 
    @{prop-name}-bottom: @y; 
  }
  .@{abbrev}-l-@{size} { 
    @{prop-name}-left: @x; 
  }
  .@{abbrev}-x-@{size} { 
    @{prop-name}-right: @x; 
    @{prop-name}-left: @x; 
  }
  .@{abbrev}-y-@{size} { 
    @{prop-name}-top: @y; 
    @{prop-name}-bottom: @y; 
  }

  .loop-sizes(@prop-name; @abbrev; @size-index - 1); /* call next iteration */
}

As you'd have noticed, I have attached the !important to the mixin call itself instead of attaching it each property. When this is done, the Less compiler automatically attaches the !important to every property and so we needn't repeat it.