On the right heading!
So now you should have full control of Sass from the command line. Let's move onto another typography challenge, headings, and sizing them correctly for best results to match the work we did with our body text.
By default, the font-size of headings in most modern browsers is as follows:
- h1 is 2em
- h2 is 1.5em
- h3 is 1.17em
- h4 is 1em
- h5 is 0.83em
- h6 is 0.67em
So you can probably tell that doesn't look quite right. So continuing from our last example where we simply multiplied our font size by a ratio of 1:15 to get what worked best, we need to do something similar. Now that will work fine on our h4, which is 1em (or rem, which is what we are using); however, all the other headings work on a different ratio to each other. Now, I've worked them out already. The good thing is we know how to get our starting value for our h2 regardless of our base font-size. It's always exactly double that. Then we can use the following to calculate the rest of the headings:
- h1 / 1.3333 = h2
- h2 / 1.2821 = h3
- h3 / 1.17 = h4
- h4 / 1.2048 = h5
- h5 / 1.2388 = h6
This is the perfect time to use some functions. While mixins looks and feel pretty much like functions, they do have some stark differences in their core functionality. Mainly, mixins return, or rather they output CSS. Functions however, only return a single value, and they MUST return a value.
So let's create a file for our functions in our helpers directory called _functions.scss
. Before we can tackle the problem of our headings, we need to be able to abstract out the $base-font-size
into a function that will dynamically generate it based on the $base-font-family
. We'll then be able to use it to improve our previous mixin, but also to include that functionality in other future mixins.
So, inside our functions file let's keep our descriptive naming convention and call our function base-font-size-calc
:
// mastering-sass/ch02/scss/helpers/_functions.scss @function base-font-size-calc($current-font-family: $base-font-family) { // 1. // Calculate the $base-font-size based on // what the current $base-font-famliy is... }
We've actually already solved this problem in a way. Remember in our base-font-sizing
mixin we had font-size: $base-font-size * 1.15
? That's basically what we're doing here. Simply abstracting that functionality out into a function. However, we can actually simplify our if
statement in our function:
//mastering-sass/scss/helpers/_functions.scss @function base-font-size-calc($current-font-family: $base-font-family) { @if $current-font-family == $base-font-family-serif { // If the family is serif we need to increase the font size... @return $base-font-size * 1.15; } @else { // ...otherwise we can leave it at the default @return $base-font-size; } }
So now we can include our functions just above our mixin include (seeing as our mixins will be using it), and below our variables (seeing as pretty much everything depends on our variables):
// mastering-sass/ch02/scss/style.scss
$base-font-family-sans: sans-serif;
$base-font-family-serif: serif;
$base-font-family-code: monospace;
$base-font-size: 1rem;
$base-line-height: 1.5;
$base-font-family: $base-font-family-serif;
@import "helpers/functions";
@import "helpers/mixins";
We can now update our base-font-family-sizing
mixin like so:
// mastering-sass/ch02/scss/helpers/_mixins.scss
@mixin base-font-family-sizing($current-font-family: $base-font-family)
{
font-size: base-font-sizes-calc();
font-family: $current-font-family;
line-height: $base-line-height;
margin-bottom: $base-font-size * $base-line-height;
}
With that one function we've completely removed the logic from our mixin. Our mixin now does what it's meant to do, output CSS, while our function handles the logic, or the function-ality if you will. This keeps everything DRY (Don't Repeat Yourself) and means our mixin and function each have a single responsibility.
A better If
No, that's not the name for a psychological thriller novel. So what do I mean by "a better if
" I hear you ask? Well, we can actually shorten our function even further. Right now it's about 5-6 lines depending on how you write your if statements. I bet we can get it down to 1 line! That's right, I'm talking about a ternary if
statement.
Now, while it's true that Sass doesn't have a traditional ternary operator, like Ruby, PHP or JavaScript do...it does have a function which does what we need. So let's take a look at that now. In our base-font-sizes-calc
function we can actually rewrite it like this:
//mastering-sass/scss/helpers/_functions.scss @function base-font-size-calc($current-font-family: $base-font-family) { @return if($current-font-family == $base-font-family-serif, $base-font-size * 1.15, $base-font-size); }
How cool is that! If that doesn't make you feel good, nothing will. So let's break down exactly how the if()
function works. The function takes three arguments. The first is called the expression. It's the condition which must evaluate to either true
or false
. The second argument is what is returned if the expression passes, and the third argument is returned otherwise:
$name: "Luke"; content: if($name == "Luke", "Your name is Luke", "Your name is not Luke");
The preceding code would obviously result in the following:
content: "Your name is Luke";
Anything else would result in, you guessed it, the following:
content: "Your name is not Luke";
So, once you've tested everything still works and changing the variable $base-font-family
still gives the expected results, we can move onto our next function, calculating each of our header sizes correctly.
Our heading sizes mixin
So let's jump back into our _functions.scss file and start our base-headings-sizes-calc
function. Remember back at the start of this section I mentioned the two most important things we know to get us started with this function?
- We know our
h4
should always be the same font-size as our paragraph font-size - We know our
h1
is exactly double ourh4
// mastering-sass/ch02/scss/helpers/_functions.scss @function base-heading-sizes-calc() { $h4-font-size: base-font-size-calc(); $h1-font-size: $h4-font-size * 2; @return $h1-font-size; }
So, this is a good point to make sure we're getting the right values returned. To be exact, when:
$base-font-family: $base-font-family-sans; // Results in h1 {font-size: 2rem}
and...
$base-font-family: $base-font-family-serif; // Results in h1 {font-size: 2.3rem}
...then we can safely move on. We're sure our function is giving us what we expect, so let's start calculating the other headings font-sizes.
We already know the formula:
- h1 / 1.3333 = h2
- h2 / 1.2821 = h3
- h3 / 1.17 = h4
- h4 / 1.2048 = h5
- h5 / 1.2388 = h6
Also, now we have our h1
, so we can simply implement this formula using variables like so:
// mastering-sass/ch02/scss/helpers/_functions.scss @function base-heading-sizes-calc() { $h4-font-size: base-font-size-calc(); $h1-font-size: $h4-font-size * 2; $h2-font-size: $h1-font-size / 1.3333; $h3-font-size: $h2-font-size / 1.2821; $h5-font-size: $h4-font-size / 1.2048; $h6-font-size: $h5-font-size / 1.2388; @return $h1-font-size; }
Again, I would advise checking your values against a good old-fashioned calculator, but I'll leave that up to you this time.
Our next problem is how do we get the right values out when we need them? For instance, we need to get the $h3-font-size
when we're in our h3
CSS rule and the $h2-font-size
when we're in our h2
CSS rule. To do this we'll need to be able to tell our function when we want a specific value. So we simply add a variable with a number to represent the heading we want:
// mastering-sass/ch02/scss/helpers/_functions.scss
@function base-heading-sizes-calc($heading: 2) {
$h4-font-size: base-font-size-calc();
$h1-font-size: $h4-font-size * 2;
$h2-font-size: $h1-font-size / 1.3333;
$h3-font-size: $h2-font-size / 1.2821;
$h5-font-size: $h4-font-size / 1.2048;
$h6-font-size: $h5-font-size / 1.2388;
@return $h1-font-size;
}
So I've added a variable called $heading
and given a default value of 2
, seeing as (in my opinion) h2
is going to be the most used heading overall. That or h3
.
Now, we could start writing an obscene amount of @if @elseif
to check our variable and return the correct variable, but a list would be much cleaner. We'll make a list containing the six heading sizes in order from $h1-font-size
to $h6-font-size
:
// mastering-sass/ch02/scss/helpers/_functions.scss
@function base-heading-sizes-calc($heading: 2) {
$h4-font-size: base-font-size-calc();
$h1-font-size: $h4-font-size * 2;
$h2-font-size: $h1-font-size / 1.3333;
$h3-font-size: $h2-font-size / 1.2821;
$h5-font-size: $h4-font-size / 1.2048;
$h6-font-size: $h5-font-size / 1.2388;
$headings: ($h1-font-size, $h2-font-size, $h3-font-size, $h4-font-size, $h5-font-size, $h6-font-size);
@return $h1-font-size;
}
Our final task is getting the correct item from our $headings
list. We can do this with the Sass function nth()
. The nth()
functions takes two parameters, first the list, which in our case is $headings
, and second, the index, or position of the item in the list we want to retrieve.
To accomplish this, we simply update our function to return nth ($headings
, $heading
):
// mastering-sass/ch02/scss/helpers/_functions.scss
@function base-heading-sizes-calc($heading: 2) {
$h4-font-size: base-font-size-calc();
$h1-font-size: $h4-font-size * 2;
$h2-font-size: $h1-font-size / 1.3333;
$h3-font-size: $h2-font-size / 1.2821;
$h5-font-size: $h4-font-size / 1.2048;
$h6-font-size: $h5-font-size / 1.2388;
$headings: ($h1-font-size, $h2-font-size, $h3-font-size, $h4-font-size, $h5-font-size, $h6-font-size);
@return nth($headings, $heading);
}