Search code examples
loopsdictionarysasseach

Sass Each Map loop on modifier classes with a default variant


I'm having trouble to avoid having duplicate code.

I have an element which can have size modifiers to alter the appearance of the element: sm, md, lg. Let's say my element is called .element and my size modifier classes are called .size-sm, .size-md and .size-lg.

These size classes are not required, however. In this case the element should use the properties from the md class as a default. In other words: .element and .element.size-md should give the same results.

My Sass Map is as followed:

$elementSizes: (
  sm: (
    fontSize: 0.875rem,
    height: 42px,
  ),
  md: (
    fontSize: 1rem,
    height: 48px,
  ),
  lg: (
    fontSize: 1.125rem,
    height: 54px,
  ),
);

My loop is currently as followed:

.element {
  @each $size, $values in $elementSizes {
    @if $size == md {
      &,
      &.size-#{$size} {
        font-size: map-get($values, fontSize);
        height: map-get($values, height);
      }
    } @else {
      &.size-#{$size} {
        font-size: map-get($values, fontSize);
        height: map-get($values, height);
      }
    }
  }
}

As you can see, I check if the object of the loop is called 'md' and then also apply its values to the parent element using the &. But it requires for me to have all properties applied twice, which I want to avoid. There is absolutely no difference between the properties applied in the if or in the else.

Any help in trying to optimize this is appreciated!


Solution

  • You have multiple possibilities to do this. For example:

    1. With append()

    @use 'sass:list';
    
    .element {
      @each $size, $values in $elementSizes {
        $selector: "&.size-#{$size}";
        
        @if $size == md {
          $selector: list.append("&", $selector, $separator: comma); 
        }
        
        #{$selector} {
          font-size: map-get($values, fontSize);
          height: map-get($values, height);
        }
      }
    }
    

    2. With a ternary operator

    .element {
      @each $size, $values in $elementSizes {
        $default: if($size == md, "&,", "");
        
        #{$default}&.size-#{$size} {
          font-size: map-get($values, fontSize);
          height: map-get($values, height);
        }
      }
    }