Where to Discuss?

Local Group

Preface

Goal: Generate custom spacing classes step by step, utilizing lessjs recursive capability to achieve loop.

Even when utilizing CSS Framework, theme making require to create custom CSS. One of my need is spacing class helper, which is not available in Semantic UI. This spacing class can be made utilizing LessJS.

lessjs: script

These guidance will save you a lot of coding time. And also tons of typing.

Reading

All code below are ported from the previous SASS article:

CSS Preprocessor

SASS/SCSS is one alternative among CSS preprocessor family.

Illustration: CSS Preprocessor


1: Simple Loop with Recursive

Imagine that we need a sequence of classes to handle margin, similar like CSS below:

.m-1 {
  margin: 1px;
}

This can be achieved with this recursive function

.loop(@cursor) when (@cursor > 0) {
  .loop((@cursor - 1)); // next iteration
  ...
}

CSS Class Naming

We want to achieve this class naming

  • .m-@{number}

LESS Source

For comfort reason, we also need to define intial value for the loop.

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));

  // code for each iteration
  .m-@{cursor}{
    margin: (1px * @cursor); 
  }
}

// launch the loop
.loop(3);

Variable Interpolation

Inside the loop we have this @{…} code. This is variable interpolation.

We use variable interpolation, when we want to extract value into the css.

CSS Result

The @cursor variable takes care the rest, so now we have this result below:

.m-1 {
  margin: 1px;
}
.m-2 {
  margin: 2px;
}
.m-3 {
  margin: 3px;
}

Limitation

The issue with .loop(@cursor) is we cannot use interval. We can have this [1, 2, 3, 4, 5] sequence. But we cannot have this [5, 10, 15, 20, 25] sequence.


2: More Loop Tricks

To solve [5, 10, 15, 20, 25] sequence, we can utilize .foo(@number) function, inside .loop(@cursor) recursive function. The .foo(@number) does the actual printing.

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));

  // actual printing
  .foo((@cursor * 5))
}

CSS Class Naming

We still want to achieve this class naming

  • .m-@{number}

LESS Source

Consider this .loop(@cursor) loop below:

.foo(@number) {
  .m-@{number}{
    margin: (1px * @number); 
  }
}

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));

  // actual printing
  .foo((@cursor * 5))
}

// launch the loop
.loop(5);

CSS Result

.m-5 {
  margin: 5px;
}
.m-10 {
  margin: 10px;
}
.m-15 {
  margin: 15px;
}
.m-20 {
  margin: 20px;
}
.m-25 {
  margin: 25px;
}

3: Each

Before facing complex situation, consider a simple example.

Challenge

Now we have challenge that margin property has these variants:

  • margin,

  • margin-top,

  • margin-bottom,

  • margin-left,

  • margin-right

For simplicity reason, I exclude the original margin variant.

CSS Class Naming

We want to achieve this class naming

  • .m-@{side}-5

List Declaration

Less variable can handle a list.

@sides: top, bottom, left, right;

Accessing List

This list can be accessed by using @each iterator.

each(@sides, {
  ...@{value}...
});

LESS Source

Consider accessing @sides by this example below:

// property
@sides: top, bottom, left, right;

each(@sides, {
  .m-@{value}-5 {
    margin-@{value}: 5px;
  }
});

CSS Result

Now we have this:

.m-top-5 {
  margin-top: 5px;
}
.m-bottom-5 {
  margin-bottom: 5px;
}
.m-left-5 {
  margin-left: 5px;
}
.m-right-5 {
  margin-right: 5px;
}

This is ugly, because we want .m-t-5, instead of .m-top-5.


4: Each with Pairs

Less has solution for this issue.

CSS Class Naming

We want to achieve this class naming

  • .m-@{abbreviation}-5

List Declaration

Luckily, less allow us to have pairs in list. Now we have CSS sub-property as below:

@sides: {top: t; bottom: b; left: l; right: r}

LESS Source

Consider rewrite previous code.

// property: abbreviation
@sides: {top: t; bottom: b; left: l; right: r}

each(@sides, {
  .m-@{value}-5 {
    margin-@{key}: 5px;
  }
})

CSS Result

Now we have what wee need.

.m-t-5 {
  margin-top: 5px;
}
.m-b-5 {
  margin-bottom: 5px;
}
.m-l-5 {
  margin-left: 5px;
}
.m-r-5 {
  margin-right: 5px;
}

Notice that I use .m-t-5, instead of .mt-5, to differ from bootstrap spacing classes.


5: Each in Loop

Consider leverage to a more complex situation. We need a sequence of each classes.

CSS Class Naming

We want to achieve this class naming

  • .m-@{subname}-@{number}

Skeleton

We put each iterator inside .foo function.

.foo(@number) {
  each(@sides, {
    ...
  })
}

LESS Source

// sub-property: abbreviation
@sides: {top: t; bottom: b; left: l; right: r}

.foo(@number) {
  each(@sides, {
    .m-@{value}-@{number} {
      margin-@{key}: (1px * @number);
    }
  })
}

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));

  // actual printing
  .foo((@cursor * 5))
}

// launch the loop
.loop(5);

CSS Result

.m-t-5 {
  margin-top: 5px;
}
.m-b-5 {
  margin-bottom: 5px;
}
.m-l-5 {
  margin-left: 5px;
}
.m-r-5 {
  margin-right: 5px;
}
.m-t-10 {
  margin-top: 10px;
}
.m-b-10 {
  margin-bottom: 10px;
}
.m-l-10 {
  margin-left: 10px;
}
.m-r-10 {
  margin-right: 10px;
}
.m-t-15 {
  margin-top: 15px;
}
.m-b-15 {
  margin-bottom: 15px;
}
.m-l-15 {
  margin-left: 15px;
}
.m-r-15 {
  margin-right: 15px;
}
.m-t-20 {
  margin-top: 20px;
}
.m-b-20 {
  margin-bottom: 20px;
}
.m-l-20 {
  margin-left: 20px;
}
.m-r-20 {
  margin-right: 20px;
}
.m-t-25 {
  margin-top: 25px;
}
.m-b-25 {
  margin-bottom: 25px;
}
.m-l-25 {
  margin-left: 25px;
}
.m-r-25 {
  margin-right: 25px;
}

6: Nested Each in Loop

CSS Class Naming

We want to achieve this class naming

  • .@{name}-@{subname}-@{number}

List Declaration

We define CSS property list:

  • property: margin and padding

  • each has sub-property: top, bottom, left, right.

@sides: {top: t; bottom: b; left: l; right: r}
@properties: {margin: m; padding: p}

Skeleton

We put both each iterator, inside .loop(@cursor) loop. But remember that we have outer loop and inner loop. The inner loop lies inside .foo(@number, @prop, @abbr).

.foo(@number, @prop, @abbr) {
  each(@sides, {
    ...
  })
}

.loop(@cursor) when (@cursor > 0) {
  ...
  
  each(@properties, {
    .foo((@cursor * 5), @key, @value)
  })  
}

LESS Source

// sub-property: abbreviation
@sides: {top: t; bottom: b; left: l; right: r}
@properties: {margin: m; padding: p}

.foo(@number, @prop, @abbr) {
  each(@sides, {
    .@{abbr}-@{value}-@{number} {
      @{prop}-@{key}: (1px * @number);
    }
  })
}

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));
  
  each(@properties, {
    // actual printing
    .foo((@cursor * 5), @key, @value)
  })  
}

// launch the loop
.loop(5);

CSS Result

Now we have both margin and padding.

.m-t-5 {
  margin-top: 5px;
}
.m-b-5 {
  margin-bottom: 5px;
}
.m-l-5 {
  margin-left: 5px;
}
.m-r-5 {
  margin-right: 5px;
}
.p-t-5 {
  padding-top: 5px;
}
.p-b-5 {
  padding-bottom: 5px;
}
.p-l-5 {
  padding-left: 5px;
}
.p-r-5 {
  padding-right: 5px;
}
.m-t-10 {
  margin-top: 10px;
}
.m-b-10 {
  margin-bottom: 10px;
}
.m-l-10 {
  margin-left: 10px;
}
.m-r-10 {
  margin-right: 10px;
}
.p-t-10 {
  padding-top: 10px;
}
.p-b-10 {
  padding-bottom: 10px;
}
.p-l-10 {
  padding-left: 10px;
}
.p-r-10 {
  padding-right: 10px;
}
.m-t-15 {
  margin-top: 15px;
}
.m-b-15 {
  margin-bottom: 15px;
}
.m-l-15 {
  margin-left: 15px;
}
.m-r-15 {
  margin-right: 15px;
}
.p-t-15 {
  padding-top: 15px;
}
.p-b-15 {
  padding-bottom: 15px;
}
.p-l-15 {
  padding-left: 15px;
}
.p-r-15 {
  padding-right: 15px;
}
.m-t-20 {
  margin-top: 20px;
}
.m-b-20 {
  margin-bottom: 20px;
}
.m-l-20 {
  margin-left: 20px;
}
.m-r-20 {
  margin-right: 20px;
}
.p-t-20 {
  padding-top: 20px;
}
.p-b-20 {
  padding-bottom: 20px;
}
.p-l-20 {
  padding-left: 20px;
}
.p-r-20 {
  padding-right: 20px;
}
.m-t-25 {
  margin-top: 25px;
}
.m-b-25 {
  margin-bottom: 25px;
}
.m-l-25 {
  margin-left: 25px;
}
.m-r-25 {
  margin-right: 25px;
}
.p-t-25 {
  padding-top: 25px;
}
.p-b-25 {
  padding-bottom: 25px;
}
.p-l-25 {
  padding-left: 25px;
}
.p-r-25 {
  padding-right: 25px;
}

7: Final

Now comes the final result.

CSS Class Naming

We want to achieve both class naming

  • .@{name}-@{number}

  • .@{name}-@{subname}-@{number}

Skeleton

We put more stuff in .foo(@number, @prop, @abbr) function.

.foo(@number, @prop, @abbr) {
  .@{abbr}-@{number} {
    @{prop}: (1px * @number) !important;
  }

  each(@sides, {
    .@{abbr}-@{value}-@{number} {
      @{prop}-@{key}: (1px * @number) !important;
    }
  })
}

LESS Source

// sub-property: abbreviation
@properties: {margin: m; padding: p}
@sides: {top: t; bottom: b; left: l; right: r}

.foo(@number, @prop, @abbr) {
  .@{abbr}-@{number} {
    @{prop}: (1px * @number) !important;
  }

  each(@sides, {
    .@{abbr}-@{value}-@{number} {
      @{prop}-@{key}: (1px * @number) !important;
    }
  })
}

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));
  
  each(@properties, {
    // actual printing
    .foo((@cursor * 5), @key, @value)
  })
  
}

// launch the loop
.loop(5);

Notice that I also put !important in css value.

CSS Result

.m-5 {
  margin: 5px !important;
}
.m-t-5 {
  margin-top: 5px !important;
}
.m-b-5 {
  margin-bottom: 5px !important;
}
.m-l-5 {
  margin-left: 5px !important;
}
.m-r-5 {
  margin-right: 5px !important;
}
.p-5 {
  padding: 5px !important;
}
.p-t-5 {
  padding-top: 5px !important;
}
.p-b-5 {
  padding-bottom: 5px !important;
}
.p-l-5 {
  padding-left: 5px !important;
}
.p-r-5 {
  padding-right: 5px !important;
}
.m-10 {
  margin: 10px !important;
}
.m-t-10 {
  margin-top: 10px !important;
}
.m-b-10 {
  margin-bottom: 10px !important;
}
.m-l-10 {
  margin-left: 10px !important;
}
.m-r-10 {
  margin-right: 10px !important;
}
.p-10 {
  padding: 10px !important;
}
.p-t-10 {
  padding-top: 10px !important;
}
.p-b-10 {
  padding-bottom: 10px !important;
}
.p-l-10 {
  padding-left: 10px !important;
}
.p-r-10 {
  padding-right: 10px !important;
}
.m-15 {
  margin: 15px !important;
}
.m-t-15 {
  margin-top: 15px !important;
}
.m-b-15 {
  margin-bottom: 15px !important;
}
.m-l-15 {
  margin-left: 15px !important;
}
.m-r-15 {
  margin-right: 15px !important;
}
.p-15 {
  padding: 15px !important;
}
.p-t-15 {
  padding-top: 15px !important;
}
.p-b-15 {
  padding-bottom: 15px !important;
}
.p-l-15 {
  padding-left: 15px !important;
}
.p-r-15 {
  padding-right: 15px !important;
}
.m-20 {
  margin: 20px !important;
}
.m-t-20 {
  margin-top: 20px !important;
}
.m-b-20 {
  margin-bottom: 20px !important;
}
.m-l-20 {
  margin-left: 20px !important;
}
.m-r-20 {
  margin-right: 20px !important;
}
.p-20 {
  padding: 20px !important;
}
.p-t-20 {
  padding-top: 20px !important;
}
.p-b-20 {
  padding-bottom: 20px !important;
}
.p-l-20 {
  padding-left: 20px !important;
}
.p-r-20 {
  padding-right: 20px !important;
}
.m-25 {
  margin: 25px !important;
}
.m-t-25 {
  margin-top: 25px !important;
}
.m-b-25 {
  margin-bottom: 25px !important;
}
.m-l-25 {
  margin-left: 25px !important;
}
.m-r-25 {
  margin-right: 25px !important;
}
.p-25 {
  padding: 25px !important;
}
.p-t-25 {
  padding-top: 25px !important;
}
.p-b-25 {
  padding-bottom: 25px !important;
}
.p-l-25 {
  padding-left: 25px !important;
}
.p-r-25 {
  padding-right: 25px !important;
}

Now you can use this spacing less in your project.


8: Update: XY

For practical reason, I have to update this article.

  • Start form zero .0, instead of 5.

  • Consider X, and Y just like bootstrap.

.m-y-5 {
  margin-top: 5px !important;
  margin-bottom: 5px !important;
}

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

The drawback is, I have bigger stylesheet size.

List Declaration

Luckily, less allow us to have pairs in list. Now we have CSS sub-property as below:

@properties: {margin: m; padding: p}
@sides: {top: t; bottom: b; left: l; right: r}
@sidesy: top, bottom;
@sidesx: left, right;

And the foo function is a little bit different:

.foo(@number, @prop, @abbr) {
  .@{abbr}-@{number} {
    @{prop}: (1px * @number) !important;
  }

  each(@sides, {
    .@{abbr}-@{value}-@{number} {
      @{prop}-@{key}: (1px * @number) !important;
    }
  })

  .@{abbr}-y-@{number} {
    each(@sidesy, {
      @{prop}-@{value}: (1px * @number) !important;
    })
  }

  .@{abbr}-x-@{number} {
    each(@sidesx, {
      @{prop}-@{value}: (1px * @number) !important;
    })
  }
}

LESS Source

As a conclusion, this is the complete code.

// sub-property: abbreviation
@properties: {margin: m; padding: p}
@sides: {top: t; bottom: b; left: l; right: r}
@sidesy: top, bottom;
@sidesx: left, right;

.foo(@number, @prop, @abbr) {
  .@{abbr}-@{number} {
    @{prop}: (1px * @number) !important;
  }

  each(@sides, {
    .@{abbr}-@{value}-@{number} {
      @{prop}-@{key}: (1px * @number) !important;
    }
  })

  .@{abbr}-y-@{number} {
    each(@sidesy, {
      @{prop}-@{value}: (1px * @number) !important;
    })
  }

  .@{abbr}-x-@{number} {
    each(@sidesx, {
      @{prop}-@{value}: (1px * @number) !important;
    })
  }
}

.loop(@cursor) when (@cursor > 0) {
  // next iteration
  .loop((@cursor - 1));
  
  each(@properties, {
    // actual printing
    .foo((@cursor * 5 -  5), @key, @value)
  })
  
}

// launch the loop
.loop(6);

What is Next ?

After this loop, there is also conditional article, that you might need to read. Consider continue reading [ Less - Conditional - Color Class ].

Thank you for reading.


Conclusion

These spacing classes is ready to serve.

lessjs: output

What do you think ?