htmlcsssass

Dynamic Spacing classes with SASS


Hi All!

I'm working with a sass file that has a looooooong set of hard-coded helper classes for padding and margins. Because we're working with scss here, this is redundant and can be solved with a sass function or something.
The trouble I'm having is actually writing the fancy sass to do the heavy lifting on this, because this requires... math. gasp


It currently looks like this...

// Spacing
$spacer: 1rem;

// Margin Helpers
.m-0 {margin: 0;}
.m-1 {margin: ($spacer * .25);}
.m-2 {margin: ($spacer * .5);}
.m-3 {margin: ($spacer);}
.m-4 {margin: ($spacer * 1.5);}
.m-5 {margin: ($spacer * 3);}

.mt-0 {margin-top: 0;}
.mt-1 {margin-top: ($spacer * .25);}
.mt-2 {margin-top: ($spacer * .5);}
.mt-3 {margin-top: ($spacer);}
.mt-4 {margin-top: ($spacer * 1.5);}
.mt-5 {margin-top: ($spacer * 3);}

.ml-0 {margin-left: 0;}
.ml-1 {margin-left: ($spacer * .25);}
.ml-2 {margin-left: ($spacer * .5);}
.ml-3 {margin-left: ($spacer);}
.ml-4 {margin-left: ($spacer * 1.5);}
.ml-5 {margin-left: ($spacer * 3);}

Now imagine this ^^ with every side and spacing, all hard-coded, and repeated for padding helpers. Yeah, my jaw is on the ground too.

I'd like to turn the above mess, into a sass function, mixin, whatever would be ideal for this conundrum. I've scoured the depths of the internet and have found a lot that are either more complicated than necessary, or can't do the math I need. I'm not that experienced with scss functions and mixins so don't bully me just yet.


I was experimenting for like 3 hours until I finally gave up; during which I had found and was reading a few articles that may be able to help but I couldn't wrap my noodle of a brain around it quite, so I'll include those below.

SASS Margin and Padding Helpers Loop. Generates .m-t-10 type helper classes.

How to dynamically build helper classes using SCSS


I'll give you a giant bear of a virtual hug if you can help me fix this disgrace of a sass file :)


Thanks in advance!
~ Josh


Solution

  • Here's a pretty simple mixin to do what you want, with explanatory comments:

    @mixin generate($prefix, $property) {
      // List of sizes to generate for each
      $sizes: [0, .25, .5, 1, 1.5, 3];
      // Spacing to multiply the sizes by
      $spacing: 1rem;
      
      // Loop through all of the sizes(we use @for rather than @each, as we want access to the index)
      @for $i from 1 through length($sizes) {
        // Get the size for the current index
        $size: nth($sizes, $i);
        
        // Create the rule, note that sass uses 1-based indexing, so the first item has an index of 1, not 0
        .#{$prefix}-#{$i - 1} {
          #{$property}: $spacing * $size;
        }
      }
    }
    

    The usage of this mixin would look like so:

    @include generate(ml, margin-left);
    

    And would compile to:

    .ml-0 {
      margin-left: 0rem;
    }
    
    .ml-1 {
      margin-left: 0.25rem;
    }
    
    .ml-2 {
      margin-left: 0.5rem;
    }
    
    .ml-3 {
      margin-left: 1rem;
    }
    
    .ml-4 {
      margin-left: 1.5rem;
    }
    
    .ml-5 {
      margin-left: 3rem;
    }
    

    You can play around with this in the SASS playground.

    When it comes to generating these for a large number of properties, you could take it another step, and use an @each:

    $rules: [
      ml margin-left,
      mt margin-top,
      mb margin-bottom,
      mr margin-right,
    ];
    
    @each $label, $prop in $rules {
      @include generate($label, $prop);
    }
    

    And another SASS playground for your convenience.

    While you could go deeper and just loop over the -top, -bottom, etc so you just have to specify a property and everything else is generated, I don't know your use case, and you can figure it out.