Search code examples
for-loopsassgridmedia-querieseach

@for and @each to generate grid column classes in Sass


I'm trying find the way to create dynamic column classes in Sass for the website I'm working on. And this is what I have so far:

$breakpoints: (small: 0, medium: 640px, large: 1024px);
$grid-columns: 12;

@for $i from 1 through $grid-columns {
  @each $key, $value in $breakpoints {
    .#{$key}-#{$i} { width: (100% / $grid-columns) * $i }
  }
}

This is what I get after compilation:

.small-1 { width: 8.33333%; }

.medium-1 { width: 8.33333%; }

.large-1 { width: 8.33333%; }

.small-2 { width: 16.66667%; }

.medium-2 { width: 16.66667%; }

.large-2 { width: 16.66667%; }

...

But in fact, this is what I would like to get:

.small-1, .medium-1, .large-1 { width: 8.33333%; }

.small-2, .medium-2, .large-2 { width: 16.66667%; }

...

Any ideas out there? :)


Solution

  • Ok, so I made a mistake and didn't think much ahead, because in fact, what I was actually going to need was the following output:

    .small-1 { width: 8.33333%; }
    .small-2 { width: 16.66667%; }
    .small-3 { width: 25%; }
    ...
    
    @media only screen and (min-width: 640px) {
      /* line 43, ../scss/_grid.scss */
      .medium-1 { width: 8.33333%; }
      .medium-2 { width: 16.66667%; }
      .medium-3 { width: 25%; }
      ...
    }
    @media only screen and (min-width: 1024px) {
      .large-1 { width: 8.33333%; }
      .large-2 { width: 16.66667%; }
      .large-3 { width: 25%; }
      ...
    }
    

    And I achieved that throught the following code:

    $breakpoints: (small: 0, medium: 640px, large: 1024px);
    $grid-columns: 12;
    
    @each $key, $value in $breakpoints {
      @if $value == 0 {
        @for $i from 1 through $grid-columns {
          .#{$key}-#{$i} { width: (100% / $grid-columns) * $i; };
        }
      } @else if $value != 0 {
        @include breakpoint(#{$key}) {;
        @for $i from 1 through $grid-columns {
          .#{$key}-#{$i} { width: (100% / $grid-columns) * $i; };
        };
        };
      }
    }
    

    Hope this can also be helpful to someone else ;)

    Edit: Since @Ram Kumar got a little curious about the @include breakpoint(#{$key}) part, I'm adding the mixin code behind that right below. There must be a better or simpler way of doing it, but this what I could come up with. If you're looking for a simple solution, you will only need the first lines of the following:

    @mixin breakpoint($point) {
    
        // breakpoint(medium), breakpoint(large)...
        @each $key, $value in $breakpoints {
            @if $point == $key and not ($value == 0) {
                @media only screen and (min-width: #{$value}) { @content; }
            }
        }
    
        // breakpoint(small only), breakpoint(medium only), breakpoint(large only)...
        $i: 1;
        @each $key, $value in $breakpoints {
    
            $bp-only: $key only;
            $bp-keys: map-keys($breakpoints);
            $bp-values: map-values($breakpoints);
    
            @if $point == $bp-only and $value == 0 {
                @media only screen and (max-width: #{nth($bp-values, $i + 1)}) { @content; }
            }
            @if $point == $bp-only and not ($value == 0) and not ($key == nth($bp-keys, -1)) {
                @media only screen and (min-width: #{$value}) and (max-width: #{nth($bp-values, $i)}) { @content; }
            }
            @if $point == $bp-only and $key == nth($bp-keys, -1) {
                @media only screen and (min-width: #{$value}) { @content; }
            }
    
            $i: $i + 1;
        }
    
        // breakpoint(medium down), breakpoint(large down)...
        @each $key, $value in $breakpoints {
            $bp-down: $key down;
            @if $point == $bp-down and not ($value == 0) {
                @media only screen and (max-width: #{$value}) { @content; }
            }
        }
    
    }