Removing Sass Duplication Using Sass Maps

Kyle Fiedler

Recently Dan asked me to get new icons for Raleigh, New York City, and Austin added to some of our landing pages.

Original Icons

Once I created the icons, I checked out the site to put them in below the offices that have already been on the marketing pages. It looked like this:

&#boston:after {
  background: url('/img/icon_boston.png') no-repeat center top;
}

&#denver:after {
  background: url('/img/icon_denver.png') no-repeat center 42px;
}

&#sanfran:after {
  background: url('/img/icon_sf.png') no-repeat center 54px;
}

&#philly:after {
  background: url('/img/icon_philly.png') no-repeat center 42px;
}

&#stockholm:after {
  background: url('/img/icon_stockholm.png') no-repeat center 45px;
}

@media
(-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {

  &#boston:after {
    background: url('/img/icon_boston@2x.png') no-repeat center top;
    background-size: 97px 112px;
  }

  &#denver:after {
    background: url('/img/icon_denver@2x.png') no-repeat center 42px;
    background-size: 160px 71px;
  }

  &#sanfran:after {
    background: url('/img/icon_sf@2x.png') no-repeat center 54px;
    background-size: 210px 59px;
  }

  &#philly:after {
    background: url('/img/icon_philly@2x.png') no-repeat center 42px;
    background-size: 66px 85px;
  }

  &#stockholm:after {
    background: url('/img/icon_stockholm@2x.png') no-repeat center 45px;
    background-size: 93px 67px;
  }
}

Look at all that repetition! That’s almost 50 lines of Sass. Adding new offices to that list would add even more to the repetition. I wanted to get rid of all the repetition and make it really easy to add new offices if we need to in the future. I started off by putting all our offices in a variable then looped through them and used some interpolation to assign the right icon to the right city.

$offices: boston, denver, sanfran, philly, stockholm, new-york, raleigh, austin;

@each $city in $offices  {
  &##{$city}:after {
    background-image: url('/img/icon_#{$city}.png');
    background-position: center 42px;
    background-repeat: no-repeat;

    @if $city == 'boston' {
      background-position: center top;
    }

    @if $city == 'new-york' {
      background-position: center top;
    }

    @if $city == 'sanfran' {
      background-position: center 54px;
    }

    @if $city == 'austin' {
      background-position: center 24px;
    }

    @if $city == 'raleigh' {
      background-position: center 28px;
    }
  }

  @include hirez-screen {
    &##{$city}:after {
      background-image: url('/img/icon_#{$city}@2x.png');
      background-size: 97px 112px;
    }
  }
}

There’s still some repetition in that loop with different positioning for each of the icons. Each of the cities that I am overriding the position is getting two lines of positioning when one will do. I figured that there had to be a better way to handle to the differing positions in Sass too.

Luckily for me, Sass 3.3 was released with Sass maps while I was designing the new icons. Connie suggested I try putting the offices into a map with their respective y-position. I went back to my list of offices and converted it to a map.

$offices: (boston: top, denver: 42px, sanfran: 54px, philly: 42px, stockholm: 42px, new-york: top, raleigh: 28px, austin: 24px);

Then went back to my @each loop and changed it around to account for the map.

@each $city, $y-position in $offices  {
  &##{$city}:after {
    background-image: url('/img/icon_#{$city}.png');
    background-position: center $y-position;
    background-repeat: no-repeat;
  }

  @include hirez-screen {
    &##{$city}:after {
      background-image: url('/img/icon_#{$city}@2x.png');
      background-size: 97px 112px;
    }
  }
}

Now we’re talking. It’s really compact and there’s no repetition.

New Icons