DEV Community

Cover image for Responsive Font Sizes with Sass Maps and @each
Technophile
Technophile

Posted on • Edited on

Responsive Font Sizes with Sass Maps and @each

Hi, there! Today, you will learn how to create responsive font sizes with Sass maps and @each method.

I hope you enjoy it!

Let's start with a map and we can dive into more complex concepts with @each operation.

First of all, create a map that contains font sizes for 2 different screen sizes. Call them sm and lg, which stand for small and large screen sizes respectively. Both should contain the same key with different values.

If you don't know what Sass map is, it is equivalent to object in JavaScript or dictionary in Python.

_type-scale.scss

$type-scale: (
  sm: (
    300: 0.875rem,
    400: 1rem,
    500: 1.125rem,
    600: 1.375rem,
    700: 2rem,
  ),
  lg: (
    300: 1rem,
    400: 1.125rem,
    500: 1.375rem,
    600: 2rem,
    700: 2.75rem,
  ),
);
Enter fullscreen mode Exit fullscreen mode

Okay, you might probably know that one good way to create responsive font sizes in CSS is with custom properties. And, their value can be easily changed. It looks something like this:

example.css

:root {
  --fs-300: 0.875rem;
  --fs-400: 1rem;
  --fs-500: 1.125rem;
  --fs-600: 1.375rem;
  --fs-700: 2rem;
}

@media only screen and (min-width: 50em) {
  :root {
    --fs-300: 1rem;
    --fs-400: 1.125rem;
    --fs-500: 1.375rem;
    --fs-600: 2rem;
    --fs-700: 2.75rem;
  }
}
Enter fullscreen mode Exit fullscreen mode

It is easy to change the value of custom properties at any given point. However, with Sass, we have what we call variables whose value are quite tough to change. That said, we can use the superpower of Sass, which will help us to generate those custom properties. You might be saying what is the point of making things complicated even though at the end of the day we end up with these custom properties. It's true, but, Sass is like an organized version of CSS and provides features that makes the code more robust and organized.

Okay, let's get back to the topic. Our final goal is to turn the map (in _type-scale.scss) into custom properties (in example.css). Whenever you see or use Sass maps, you might probably have seen @each rule. It is because they work very well together. You can describe them as arrays with map() or forEach functions in JavaScript.

So, start by creating a @each rule. It will take 3 parameters.

  • 1st: key in the map
  • 2nd: value in the map
  • 3rd: the map itself

Note: You can name key and value whatever you want, but you need to specify which map you are using.

It looks something like this:

each-example.scss

$colors: (
  primary: #06f,
  secondary: #333,
  accent: #f70,
);

@each $key, $value in $colors {
  .text-#{$key} {
    color: $value;
  }
}
Enter fullscreen mode Exit fullscreen mode

Compiled CSS:

each-example.css

.text-primary {
  color: #06f;
}

.text-secondary {
  color: #333;
}

.text-accent {
  color: #f70;
}
Enter fullscreen mode Exit fullscreen mode

#{$key} - this weird looking thing is called Interpolation. It is used to inject values into strings. You can learn more about it here.

As you can see, Sass is a truly powerful language. If we describe CSS as JavaScript, Sass can be seen as React.

Ok, I hope you got the idea of how @each and maps work very well together. Now, let's generate our responsive font sizes.

Create a @each rule that loops through $type map and generate following output:

example.css

:root {
  --fs-300: 0.875rem;
  --fs-400: 1rem;
  --fs-500: 1.125rem;
  --fs-600: 1.375rem;
  --fs-700: 2rem;
}

@media only screen and (min-width: 50em) {
  :root {
    --fs-300: 1rem;
    --fs-400: 1.125rem;
    --fs-500: 1.375rem;
    --fs-600: 2rem;
    --fs-700: 2.75rem;
  }
}
Enter fullscreen mode Exit fullscreen mode

You need to use nested @each rule for nested maps! Also, you might need to use @if and @else rules. You can learn more about them here. Yes, we have if statements in Sass, too.:)

Okay, start with the first layer of @each rule and see what it will output.

style.scss

@use 'type-scale' as *;

:root {
  @each $key, $value in $type-scale {
    --#{$key}: $value;
  }
}
Enter fullscreen mode Exit fullscreen mode

Compiled CSS:

style.css

:root {
  --sm: $value;
  --lg: $value;
}
Enter fullscreen mode Exit fullscreen mode

Whenever you see that it returns $value in CSS, just interpolate it.

style.scss

@use 'type-scale' as *;

:root {
  @each $key, $value in $type-scale {
    --#{$key}: #{$value};
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, out of nowhere you will get an error, saying it is not valid CSS. Becuase $key is returning screen sizes (sm, lg), and $value is trying to return another map inside those keys. Let's create one more @each and see what it will return.

Dont' forget that $value is returning inner map, so the second @each should be relative to $value.

style.scss

:root {
  @each $screen-size, $sizes in $type-scale {
    @each $key, $value in $sizes {
      --fs-#{$key}: #{$value};
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

If you remember, in the first layer of @each, $key returned screen sizes (sm, lg) and $value returned another map, containing all the sizes of each key. So, I called $key as $screen-size and $value as $sizes.

Now, in our inner @each rule, we are asking it to return keys and values inside those sizes.

Compiled CSS:

style.css

:root {
  --fs-300: 0.875rem;
  --fs-400: 1rem;
  --fs-500: 1.125rem;
  --fs-600: 1.375rem;
  --fs-700: 2rem;
  --fs-300: 1rem;
  --fs-400: 1.125rem;
  --fs-500: 1.375rem;
  --fs-600: 2rem;
  --fs-700: 2.75rem;
}
Enter fullscreen mode Exit fullscreen mode

Okay, we are getting there. A few things left to do. Now, we need to find which key belongs to sm or lg screen sizes. And, here @if and @else come very handy.

So, $screen-size return screen-sizes (sm, lg). If it returns sm, just return it inside the :root. Else return it inside the media-query.

You need to use @if and @else statements after the first layer of @each rule, not the second!. If it satisfies, return the second layer of @each rule that we wrote above.

style.scss

@use 'type-scale' as *;

:root {
  @each $screen-size, $sizes in $type-scale {
    @if $screen-size == sm {
      @each $key, $value in $sizes {
        --fs-#{$key}: #{$value};
      }
    } @else {
      @media (min-width: 50em) {
        @each $key, $value in $sizes {
          --fs-#{$key}: #{$value};
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Compiled CSS:

style.css

:root {
  --fs-300: 0.875rem;
  --fs-400: 1rem;
  --fs-500: 1.125rem;
  --fs-600: 1.375rem;
  --fs-700: 2rem;
}

@media (min-width: 50em) {
  :root {
    --fs-300: 1rem;
    --fs-400: 1.125rem;
    --fs-500: 1.375rem;
    --fs-600: 2rem;
    --fs-700: 2.75rem;
  }
}
Enter fullscreen mode Exit fullscreen mode

Yes, we did it! As you might have guessed, we can add more screen sizes. And, it is a task for you if you want to give it a shot.

If you have any doubts, questions or feedback, please do consider sharing it with me. I really appreciate it and get back to you as soon as possible!

If you are new to Sass maps and @each method, I highly recommend checking out Kevin Powell's video where he explained the superpower of Sass with custom properties and utility classes. It is where I learned these cool things, too.:)


That's it! I hope you liked it!

You can get the source code from this GitHub repo

If you've liked this article, please do consider sharing it with others. It means the world to me

Top comments (0)