Oct 28, 2016

Loops With less

When I write CSS I like to use utility classes. When I write JavaScript, I like to use utility functions. Utilities are basically just small, laser focused bits of code. Underscore and Lodash provide a great set of utilities for JS, but I find that CSS utilities often have to be written from scratch.

The following snippet of code is from a LESS file I wrote a while back to generate a handful of utility classes:

// Spacing utility classes
// creates:
//  .pad-sm, .pad-md, .pad-lg, etc
//  .mar-sm, .mar-md, .mar-lg, etc
//  .pad-left-sm, .mar-left-sm, etc
//
@nil: none 0 e('!important');
// @auto 0 auto; is built into the fn below
@xs: xs 3px e('');
@sm: sm 5px e('');
@md: md 10px e('');
@lg: lg 15px e('');
@xl: xl 20px e('');
@xxl: xxl 30px e('');

@sizes: @nil, @xs, @sm, @md, @lg, @xl, @xxl;
@sides: top, right, bottom, left;

.spacer-side(@list, @size, @px, @mod, @i:1) when (@i <= length(@list)) {
  @side: extract(@list, @i);
  .pad-@{side}-@{size} {
    padding-@{side}: @px @mod;
  }
  .mar-@{side}-@{size} {
    margin-@{side}: @px @mod;
  }
  .spacer-side(@list, @size, @px, @mod, @i+1);
}

.spacers(@list, @i:1) when (@i <= length(@list)) {
  @item: extract(@list, @i);
  @key: extract(@item, 1);
  @val: extract(@item, 2);
  @mod: extract(@item, 3);
  .pad-auto-@{key} {
    padding: 0 auto;
  }
  .mar-auto-@{key} {
    margin: 0 auto;
  }
  .pad-@{key} {
    padding: @val @val * 1.5;
  }
  .mar-@{key} {
    margin: @val @val * 1.5;
  }
  .spacer-side(@sides, @key, @val, @mod);
  .spacers(@list, @i+1);
}

.spacers(@sizes);

The output looks like this:

.pad-auto-none{padding:0 auto}
.mar-auto-none{margin:0 auto}
.pad-none{padding:0}
.mar-none{margin:0}
.pad-top-none{padding-top:0!important}
.mar-top-none{margin-top:0!important}
.pad-right-none{padding-right:0!important}
.mar-right-none{margin-right:0!important}
.pad-bottom-none{padding-bottom:0!important}
.mar-bottom-none{margin-bottom:0!important}
.pad-left-none{padding-left:0!important}
.mar-left-none{margin-left:0!important}
.pad-auto-xs{padding:0 auto}
.mar-auto-xs{margin:0 auto}
.pad-xs{padding:3px 4.5px}
.mar-xs{margin:3px 4.5px}
.pad-top-xs{padding-top:3px}
.mar-top-xs{margin-top:3px}
.pad-right-xs{padding-right:3px}
.mar-right-xs{margin-right:3px}
.pad-bottom-xs{padding-bottom:3px}
.mar-bottom-xs{margin-bottom:3px}
.pad-left-xs{padding-left:3px}
.mar-left-xs{margin-left:3px}
.pad-auto-sm{padding:0 auto}
.mar-auto-sm{margin:0 auto}
.pad-sm{padding:5px 7.5px}
.mar-sm{margin:5px 7.5px}
.pad-top-sm{padding-top:5px}
.mar-top-sm{margin-top:5px}
.pad-right-sm{padding-right:5px}
.mar-right-sm{margin-right:5px}
.pad-bottom-sm{padding-bottom:5px}
.mar-bottom-sm{margin-bottom:5px}
.pad-left-sm{padding-left:5px}
.mar-left-sm{margin-left:5px}
.pad-auto-md{padding:0 auto}
.mar-auto-md{margin:0 auto}
.pad-md{padding:10px 15px}
.mar-md{margin:10px 15px}
.pad-top-md{padding-top:10px}
.mar-top-md{margin-top:10px}
.pad-right-md{padding-right:10px}
.mar-right-md{margin-right:10px}
.pad-bottom-md{padding-bottom:10px}
.mar-bottom-md{margin-bottom:10px}
.pad-left-md{padding-left:10px}
.mar-left-md{margin-left:10px}
.pad-auto-lg{padding:0 auto}
.mar-auto-lg{margin:0 auto}
.pad-lg{padding:15px 22.5px}
.mar-lg{margin:15px 22.5px}
.pad-top-lg{padding-top:15px}
.mar-top-lg{margin-top:15px}
.pad-right-lg{padding-right:15px}
.mar-right-lg{margin-right:15px}
.pad-bottom-lg{padding-bottom:15px}
.mar-bottom-lg{margin-bottom:15px}
.pad-left-lg{padding-left:15px}
.mar-left-lg{margin-left:15px}
.pad-auto-xl{padding:0 auto}
.mar-auto-xl{margin:0 auto}
.pad-xl{padding:20px 30px}
.mar-xl{margin:20px 30px}
.pad-top-xl{padding-top:20px}
.mar-top-xl{margin-top:20px}
.pad-right-xl{padding-right:20px}
.mar-right-xl{margin-right:20px}
.pad-bottom-xl{padding-bottom:20px}
.mar-bottom-xl{margin-bottom:20px}
.pad-left-xl{padding-left:20px}
.mar-left-xl{margin-left:20px}
.pad-auto-xxl{padding:0 auto}
.mar-auto-xxl{margin:0 auto}
.pad-xxl{padding:30px 45px}
.mar-xxl{margin:30px 45px}
.pad-top-xxl{padding-top:30px}
.mar-top-xxl{margin-top:30px}
.pad-right-xxl{padding-right:30px}
.mar-right-xxl{margin-right:30px}
.pad-bottom-xxl{padding-bottom:30px}
.mar-bottom-xxl{margin-bottom:30px}
.pad-left-xxl{padding-left:30px}
.mar-left-xxl{margin-left:30px}

Usage is something like this (with a little flex mixed in):

<div class="flex-column mar-md pad-sm">
  <div class="flex mar-sm pad-sm">
    Hello
  </div>
  <div class="mar-md pad-md">
    World
  </div>
</div>

The above could definitely start a war over semantics (I’ve had that debate regarding this code) but I’m more in the object oriented CSS camp (or any that values CSS highly and treats the HTML as “throwaway”).

I’ve not written a lot of Less and found the need to use recursion rather than a built-in ‘for’ or ‘each’ loop a little strange. Still, it was interesting working out a way to build nested lists and iterate through them in this new language. I intend to write the same thing in SASS soon for the sake of comparison.