• 15 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 3/13/23

Use loops in Sass to streamline your code

Loop de loop

In the last chapter, we created a mixin to expedite making various text input modifiers:

$txt-input-palette: (
    active: (
        bg: $colour-primary,
        border: $colour-primary,
        txt: $colour-white,
    ),
    focus: (
        bg: $colour-primary,
        border: $colour-primary,
        txt: $colour-white,
    ),
    invalid: (
        bg: $colour-invalid,
        border: $colour-white,
        txt: $colour-white,
    )
);

@mixin txt-input-palette($state) {
    $palette: map-get($txt-input-palette, $state);
    border: .1rem solid map-get($palette, border);
    background-color: map-get($palette, bg);
    color: map-get($palette, txt);
}

We still need to create the selector for each state and apply the mixin with the appropriate argument. This is both repetitive and tedious, and the exact thing that Sass is so great at saving us from.

In programming, there’s a concept called looping, where you can iterate through a dataset, and execute code for each item in the set.

Say what? 😨

Take a box of cookies for example. Within the packaging, there are 45 individual, delicious, chocolate chip cookies. 🍪 You’re in the kitchen and can hear them calling to you from the cupboard, and the following routine commences:

  • You crack open the packaging.

  • You debate whether you really need to eat a cookie.

  • You decide “no” but you’ve already bought them, so you might as well enjoy one, right?

  • You transport yourself to nirvana as we eat a cookie.

  • You feel a little guilty.

  • You close the package, and tell yourself it was just one cookie.

Then you open the packaging. Again. And go through the same routine, over and over again until you’ve eaten all of the cookies. Some might refer to this as a vicious cycle. I prefer to call it a loop! A guilt-ridden loop, but a loop all the same. A loop is just repeating a set of actions on items in a collection until you run out of items.

By implementing a loop with your text input mixin, Sass can automatically create the relevant selectors and rulesets for each item in the map you supply it. That sounds a lot cleaner and less laborious than manually creating a block for each of the text input states, right?

There are several types of loops available in Sass, but we’re only going to worry about one: the  @each  loop. It’s the simplest and cleanest to set up, and works seamlessly with maps. Since all of the different types of loops that Sass has to offer are going to produce the same result in the end, why not go with the most straightforward option?

When you write an @each loop in Sass, you are saying that you want to perform a task for each key/value pair in a  $map , and that is exactly how you define one:

@each $key, $value in $map {
    
}

To kick things off, define the loop with the@each keyword. Then declare each of what exactly: each$key and $value . Then from where: each $key and$value in this $map.

Sass goes to the map and and creates temporary$key and $value variables for each set that it finds. These variables exist only within that iteration of the loop. They don’t exist before or after the iteration, and are invisible to outside code.

Each and every last one

We’re going to use the @each loop to create all of the pseudo-selector rulesets for all of our.form__input label states, and fill them with their ruleset in one fell swoop!  Let's tweak our  txt-input-palette to do our bidding.

The first thing that you need to do is pass the entire$txt-input-pallete map rather than the specific $state key as the argument. Your loop will iterate through each of the keys in the map, so there's no need to name them manually:

@mixin txt-input-palette($palettes) {
    $palette: map-get($txt-input-palette, $state);
    border: .1rem solid map-get($palette, border);
    background-color: map-get($palette, bg);
    color: map-get($palette, txt);
}

You can also remove the $palette variable and its map-get() function. The @each loop will automatically get the values for each key in the map as well:

@mixin txt-input-palette($palettes) {
    
    border: .1rem solid map-get($palette, border);
    background-color: map-get($palette, bg);
    color: map-get($palette, txt);
}

 Now you can start using@each to automate your workflow!

@mixin txt-input-palette($palettes) {
    @each $state, $palette in $palettes{
        border: .1rem solid map-get($palette, border);
        background-color: map-get($palette, bg);
        color: map-get($palette, txt);
    }
}

We’ve added an @each loop that will iterate over our map,$palettes, with the variables $state  and  $palette  storing the key and value, respectively. Then, within each loop, it creates rules for the border,  background-color, and text color for each state it finds.

Only it's putting them all in a single selector:

@mixin txt-input-palette($palettes) {
    @each $state, $palette in $palettes{
        border: .1rem solid map-get($palette, border);
        background-color: map-get($palette, bg);
        color: map-get($palette, txt);
    }
}

.form {
    &__field {
        & input {
            @include txt-input-palette($txt-input-palette);
        }
    }
}
.form__field input {
  border: 0.1rem solid #15DEA5;
  background-color: #001534;
  color: #15DEA5;
  border: 0.1rem solid #15DEA5;
  background-color: #15DEA5;
  color: #fff;
  border: 0.1rem solid #15DEA5;
  background-color: #15DEA5;
  color: #fff;
  border: 0.1rem solid #fff;
  background-color: #DB464B;
  color: #fff;
}

 Not quite what we were going for.

We also wanted to create the selector for each state as we looped through the map. Let's use the ampersand,  &, and a colon, :, to join the name of each  $state  as pseudo-selectors:

@mixin txt-input-palette($palettes) {
    @each $state, $palette in $palettes{
        &:#{$state}{
            border: .1rem solid map-get($palette, border);
            background-color: map-get($palette, bg);
            color: map-get($palette, txt);
        }
    }
}

That hashtag/curly brace/$variable thing is new, but don't worry! It’s just Sass’s interpolation syntax. It allows you to use a variable’s value within a string. In this case, it’s allowing us to use the  $state variable's value as the name of the pseudo-selector.

Now, when you check out the compiled CSS, you should see a pseudo-selector for each state with the appropriate color values populating the ruleset:

.form__field input:active {
  border: 0.1rem solid #15DEA5;
  background-color: #15DEA5;
  color: #fff;
}
.form__field input:focus {
  border: 0.1rem solid #15DEA5;
  background-color: #15DEA5;
  color: #fff;
}
.form__field input:invalid {
  border: 0.1rem solid #fff;
  background-color: #DB464B;
  color: #fff;
}

Nowtxt-input-palette will generate a pseudo-selector for the state if the state isn't "inactive." Let's check and see if it's working properly:

.form__field input:active {
  border: 0.1rem solid #15DEA5;
  background-color: #15DEA5;
  color: #fff;
}
.form__field input:focus {
  border: 0.1rem solid #15DEA5;
  background-color: #15DEA5;
  color: #fff;
}
.form__field input:invalid {
  border: 0.1rem solid #fff;
  background-color: #DB464B;
  color: #fff;
}

There you go! Now if you change any of the color values or palette combinations, the text input rules will update automatically. And not only that but if you were to add a new state to  $txt-input-palette, Sass would add it to your compiled CSS without any extra work from you. How nice is that?

If you added 20 more color palettes to your map, or removed all but one, your CSS would update itself automatically. No hassle. No tedious cutting/pasting/modifying. No more looking for the hidden typo. With loops, creating and maintaining your codebase is as easy as updating the values in a map and hitting save. It doesn’t get much cleaner and easier than that!

Try it out for yourself!

Now that we have the palette for our button modifiers stored in map, let’s save ourselves a bit of work updating and maintaining them but putting Sass to work and have it generate the modifier selectors automatically based on the contents of the $btn-mods map in this interactive exercise:

  • Store our loop logic in a mixin called btn-mods with an argument for $map

  • Inside of the mixin, set up an @each loop with the variables $mod and $val for the contents of $map

  • Inside of the @each loop, use the interpolation syntax to fill in the selector after an ampersand and set of dashes: &--#{$mod}

  • Inside of the modifier selector, create a background property and give it the value of $val

  • Remove the modifier &--pink {...} and &--blue {...} selectors and their properties

  • Include the btn-mods mixin at the end of the .btn block, passing $btn-mods as the argument

Check the rendered page that the buttons display with the proper colors - you'll find the solution in this CodePen!

Our site is starting to look pretty snazzy on a monitor! But in this day and age, it also needs to look good on screens of all shapes and sizes including phones and tablets!

Coming up next, we’ll take a look at how Sass can help make our site responsive.

Let's recap!

  • A loop is a repeating a set of actions on items in a collection. 

  • Define a loop with the @each keyword, followed by the $key, the  $value, and the associated $map.

  • Interpolation syntax allows you to use a variable’s value within a string.

Example of certificate of achievement
Example of certificate of achievement