In the previous videos, we saw how to improve our workflow using Sass mixins. Now it's time to see the power of functions, and what else we can accomplish with Sass. Before getting started, the inspiration for this practice came from Una Kravets: Let’s Build a CSS Game. If you haven't seen it, I recommend you take a couple minutes to watch. Una Kravets is amazing, and that talk was great. This is what we're going to create: an 8-bit animated character. Let's get started!

The first thing we're going to do is to create our project folder. I’ll also create an assets folder with a subfolder for stylesheets. Inside the stylesheets, we’ll create a couple more folders for components and characters. Now, we create an index.html file and _variable.scss, _functions.scss, _tools.scss, _normalize.scss, which are going to be imported in application.scss like this:

@import "variables";
@import "characters/*.*";
@import "functions";
@import "tools";
@import "normalize";
@import "components/*.*";

The order is important. Notice that we are going from logic to property definition, by making variables the bottom level. Then, functions and mixins, which interact with those variables, the mid level. Lastly, the actual styling, which is the top level.

Now, open index.html and we're going to create a div with the class character-container. Then we add an h1 with title as class. Finally we create the class for our character. He’s our CTO, Adam. So, I will call it pixel-adam. Lastly we add application.css to our head.

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="assets/stylesheets/application.css">
  <title>Sass Features</title>
</head>
<body>
  <div class="character-container">
    <h1 class="title">Pixel Adam</h1>
    <div class="pixel-adam">
    </div>
  </div>
</body>
</html>

With this, we have everything we need to start styling our pixel character. Now, what we're going to do is to open our variables file and define a map of colors, which are the ones our character will use. Every color is linked to a key; we'll use these keys to set a color for each pixel. After that, we define a size for the pixels. I'll use 1rem as the default size. It's important to highlight that from now on, everytime I say pixel I'll be' referring to a pixel square of the size we just defined.

$colors: (
  "background": #3e4c61,
  "r": red,
  "b": black,
  "m": #c27a2e,
  "n": #774c2a,
  "j": #331a13,
  "f": #1c74bb,
  "g": #2db572,
  "d": #251f21,
  "s": #fdbe9f,
  "v": #e5b180,
  "z": #c69869,
  "t": transparent,
  "w": white,
  "a": #b27a5d
);

$pixel-size: 1rem !default;

Inside our characters folder, we need to create a file for pixel Adam. I'm going to call it _pixel-character.scss. Here we will do another map, but this time for the character itself. Basically we draw the character using the keys we defined before for each color:

$pixel-character: (
  adam: (
    ( t t t t t d d t t t t t t d d d d d d t t t t t t t t )
    ( t d d t d s s d t t t d d s s s s s s d d t t t t t t )
    ( d s s d d s s d t t d s s s s s s s s s d t t t t t t )
    ( d s s s d s s d d d s s s s s s s s s s s d d t t t t )
    ( t d s s s d d d d d s s s s s s s s s s s s s d t t t )
    ( d s s s d s s s d s s s s s s s s s s s s s s d t t t )
    ( d s s s d s s s d b b b b b b b b b b b b b b d t t t )
    ( d s s s s d d s d s s b b b b b b b b b b s s d t t t )
    ( t d d s s s s d s s s s b b b s s b b b s s s s d t t )
    ( t t t d d d d d s d s s s b s s s s b s s s d s d t t )
    ( t t t t d s s d s d s s s s s s s s s s s s d s d t t )
    ( t t t t d s s s d d s m m m m m m m m m m s d d t t t )
    ( t t t t t d f f f f m m m s s s s s s m m m d t t t t )
    ( t t t t t t d f f f m m m s w w w w s m m m d t t t t )
    ( t t t t t t t d f f f m m s s s s s s m m d t t t t t )
    ( t t t t t t t t d f f f m m m m m m m m f d d t t t t )
    ( t t t t t t t t d f f f f m m m m m m f f f f d t t t )
    ( t t t t t t t t t d f f f f m m m m f f f d d s d t t )
    ( t t t t t t t t t t d f f f d m m d f f d s s s d t t )
    ( t t t t t t t t t t d f f f d d d d f f d s s s d t t )
    ( t t t t t t t t t t d f f f f f f f f f d s s d t t t )
    ( t t t t t t t t t t d f f f f f f f f f f d d t t t t )
    ( t t t t t t t t t d n n n n n n n n n n n n d t t t t )
    ( t t t t t t t t t d n n n n n n n n n n n n d t t t t )
    ( t t t t t t t t d n n n n n n n n n n n n n n d t t t )
    ( t t t t t t t t d n n n n n j j j j n n n n n d t t t )
    ( t t t t t t d d d n n n n j j j j j j n n n n d d d t )
    ( t t t t t d b b b b b b b b d t t d b b b b b b b b d )
    ( t t t t t d d b b b b b b d d t t d d b b b b b b d d )
    ( t t t t t t d d d d d d d d t t t t d d d d d d d d t )
  )

If you look closely, you can see the details, like his beard and glasses. Something important to notice here: every key in the map represents a pixel, which is 1rem, and each key has its own value, a color. Which means we have 1rem of whatever color the key represents. So we just have to go through the map, replace each key for its value, and assign all that to a CSS property we can hack... But, how can we do that? Well, luckily for us, Sass has lots of useful methods that will help us. We can also use loops and conditionals, so we have everything we need to make a function that does what we want.

Now, we open our functions.scss file and define a function called build_character. This function will have two parameters: our character color map and the size of the pixels.

@function build_character($character_draw, $size) {
}

We're going to use box-shadow to draw our character. The main reason is because it’s easy to hack. Due to how it works, we can define an indefinite quantity of shadows and use any color we want. So, let’s take advantage of that. Inside our function we define a variable called $character. This will contain our character draw, which is going to be a bunch of shadows simulating pixels.

The first thing we need to draw our character is to get the total number of rows from our map. To do so we can use a Sass method called length, which returns the size of a given map. Once we have the total number of rows, we can iterate through our character map using @for:

@function build_character($character_draw, $size) {
  $character: "";
  $rows: length($character_draw);
  @for $row-num from 1 through $rows {
  }
}

We define a variable called $row-number which we’re going to use to get the values from each row. For that we use another Sass method, nth , which returns a specific item in a list. We pass our character map and our $row-number to get the complete row with all its values, which are the color keys we defined for our character. Then we iterate through that row to get the columns (which are our color keys). To determine how many times the process should repeat we use length again, which will return the total number of columns inside every row.

@function build_character($character_draw, $size) {
  $character: "";
  $rows: length($character_draw);
  @for $row-num from 1 through $rows {
    $row: nth($character_draw, $row-num);
    @for $col from 1 through length($row) {
    }
  }
}

Now that we have access to the columns, we can get every color key in our map, one by one. Let’s define a variable called $color-key. Then use nth again to get the item passing the row and the column as arguments.

@function build_character($character_draw, $size) {
  $character: "";
  $rows: length($character_draw);
  @for $row-num from 1 through $rows {
    $row: nth($character_draw, $row-num);
    @for $col from 1 through length($row) {
      $color-key: nth($row, $col);
    }
  }
}

Now we can build our shadows. We can define the horizontal and vertical position of the shadows using the first and second parameter from box-shadow. We can define the color with the third. Knowing that, we can take advantage of it. The first parameter will be equal to pixel-size * column, the second will be equal to pixel-size * row-num, and the third will be a map-get passing our colors map and current color-key as arguments.

@function build_character($character_draw, $size) {
  $character: "";
  $rows: length($character_draw);
  @for $row-num from 1 through $rows {
    $row: nth($character_draw, $row-num);
    @for $col from 1 through length($row) {
      $color-key: nth($row, $col);
      $character: $character + ($size*$col) + " " + ($size*$row-num) + " " +
        map-get($colors, $color-key);
    }
  }
}

What we're doing is creating a shadow and moving it a "pixel" to the right for each color-key contained in one row. Once there are no more color-keys in that row, we lower the vertical position of the shadows one "pixel". Then, repeat the process with the next row of color-keys until there are no more rows. To define the color of every "pixel" we’ll use map_get with the array of colors we defined before and the color-key we're getting from the loop. To link all that together, we use the + operator and empty spaces.

This isn’t going to work, but why? Well, the shadows need to be separated by commas — except for the last shadow, which can't have a comma because if it does, it'll invalidate all the shadows. We're iterating through rows and columns, so we can use a negated conditional, which will be triggered every iteration. This will verify if we aren't in the last column of the last row. If that's the case, we add a comma to our character variable. Finally we return the character, which will be our box-shadow value. We're going to use unquote because box-shadow doesn't accept strings as values.

@function build_character($character_draw, $size) {
  $character: "";
  $rows: length($character_draw);

  @for $row-num from 1 through $rows {
    $row: nth($character_draw, $row-num);

    @for $col from 1 through length($row) {
      $color-key: nth($row, $col);

      $character: $character + ($size*$col) + " " + ($size*$row-num) + " " +
        map-get($colors, $color-key);

      @if not ($col == length($row) and $row-num == $rows) {
        $character: $character + ",";
      }
    }
  }
  @return unquote($character);
}

Finally we create a _pixel-adam.scss file inside components/, and define a pseudo element after. We give it a height and width equal to the pixel-size we defined. Set position to absolute, and use our function, passing our map character and the pixel-size as arguments.

.pixel-adam {
  &:after {
    content: "";
    height: $pixel-size;
    position: absolute;
    width: $pixel-size;
    box-shadow: build_character(map-get($pixel-character, adam), $pixel-size);
  }
}

Boom! We have our 8-bit character! This was a fun way to see how functions works in Sass. We still have to do a lot of stuff to improve our character. So, we're going to stop here, and continue in the next video. See ya!