The power of lists and maps in SASS

If you find yourself repeating a lot of similar rules when writing stylesheets or your needs go beyond simple variables think of lists and maps. I just discovered how useful lists and maps can be and therefore share a few examples.


Lists

Let's have each paragraph displayed in a different color by reading colors from a list.

$paragraph-colors: blue, red, pink;

@each $color in $paragraph-colors {
  $i: index($paragraph-colors, $color);
  p:nth-child(#{$i}) {
    color: #{$color};
  }
}

Result:

p:nth-child(1) {
  color: blue; }
p:nth-child(2) {
  color: red; }
p:nth-child(3) {
  color: pink; }


Maps

Here is a basic example of a map in action.

$breakpoints: (
  xs: 0,
  sm: 544px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

@media screen and (min-width: map-get($breakpoints, 'md')) {
  body {
    font-size: 1.125em;
  }
}

Result:

@media screen and (min-width: 768px) {
  body {
    font-size: 1.125em; } }


Maps, a practical example

I'll be creating a group of .hidden-* helper classes that will allow to hide content depending on viewport size. If you've worked with Bootstrap it's likely you are familiar with responsive utilities that work exactly in this way.

$breakpoints: (
  xs: 0,
  sm: 544px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

@each $name, $breakpoint in $breakpoints {
  @media screen and (min-width: $breakpoint) {
    .hidden-#{$name} {
      display: none;
    }
  }
}

Result:

@media screen and (min-width: 0) {
  .hidden-xs {
    display: none; } }
@media screen and (min-width: 544px) {
  .hidden-sm {
    display: none; } }
@media screen and (min-width: 768px) {
  .hidden-md {
    display: none; } }
@media screen and (min-width: 992px) {
  .hidden-lg {
    display: none; } }
@media screen and (min-width: 1200px) {
  .hidden-xl {
    display: none; } }


Maps, a practical example — continued

The first attempt to create hidden-* helper classes with SASS was partly successful, however the @media declarations will also need 'max-width' defined. In the rewritten snippet I will be using 'map-keys' and 'map-values' to access map elements at a specific index.

$breakpoints: (
  xs: 0,
  sm: 544px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

@mixin hidden($breakpoint) {
  .hidden-#{$breakpoint} {
    display: none;
  }
}

@for $i from 1 through length($breakpoints) {
  $breakpoint: nth(map-keys($breakpoints), $i);
  $min-width: nth(map-values($breakpoints), $i);
  $next-index: $i + 1;

  @if $next-index <= length($breakpoints) {
    $max-width: nth(map-values($breakpoints), $next-index) - 1px;
    @media (min-width: $min-width) and (max-width: $max-width) {
      @include hidden($breakpoint);
    }
  }
  @else {
    @media (min-width: $min-width) {
      @include hidden($breakpoint);
    }    
  }
}

Result:

@media (min-width: 0) and (max-width: 543px) {
  .hidden-xs {
    display: none; } }
@media (min-width: 544px) and (max-width: 767px) {
  .hidden-sm {
    display: none; } }
@media (min-width: 768px) and (max-width: 991px) {
  .hidden-md {
    display: none; } }
@media (min-width: 992px) and (max-width: 1199px) {
  .hidden-lg {
    display: none; } }
@media (min-width: 1200px) {
  .hidden-xl {
    display: none; } }

That's better!


Lists + Maps

There is nothing stopping you from using lists together with maps for even more glorious results. :)

$text-decoration: (
  'underline': ('cat-1', 'cat-2', 'cat-3'),
  'none': ('dog-1', 'dog-2', 'dog-3')
);

@each $decoration, $list in $text-decoration {
  @each $class in $list {
    .#{$class} {
      text-decoration: #{$decoration};
    }
  }
}

Result:

.cat-1 {
  text-decoration: underline; }
.cat-2 {
  text-decoration: underline; }
.cat-3 {
  text-decoration: underline; }
.dog-1 {
  text-decoration: none; }
.dog-2 {
  text-decoration: none; }
.dog-3 {
  text-decoration: none; }
Did you like this post?
Previous post

Handling Braintree webhooks in Laravel Cashier

Laravel Cashier is already handling Braintree's 'subscription_canceled' and 'subscription_expired' webhooks and will cancel a subscription when any of those webhooks get triggered. The webhooks are handled by WebhookController, which can be easily extended to add support for other kinds of webhooks.