Using SCSS mixins to render repetitive selectors
In a project that I’m currently working on I needed per heading different font sizes and line heights per breakpoint.
I had the following things to keep in mind:
- the designers provided me font-sizes defined in
px
while I wanted to userem
. - There were multiple themes, so the font sizes and line heights differ per theme.
At first I had to write a mixin that returned font-size
and line-height
properties, and converted pixel values to rem values:
@mixin fontsize-lineheight($font-size, $line-height, $base: 16px) {
$remsize: ($font-size / $base);
font-size: #{$remsize}rem;
line-height: $line-height / $font-size;
}
However I wanted line-height
to be unit-less, so I had to convert the variables to unit-less values using the following @function
:
@function strip-units($value) {
@return $value / ($value * 0 + 1);
}
(source: this Stackoverflow answer)
So the final result of the mixin was:
@mixin fontsize-lineheight($font-size, $line-height, $base: 16px) {
$remsize: (strip-units($font-size) / strip-units($base));
font-size: #{$remsize}rem;
line-height: strip-units($line-height / $font-size);
}
Now this mixin was ready to use for every heading:
h1, .h1 {
@include fontsize-lineheight(30px, 40px);
@include media-breakpoint-up(md) {
@include fontsize-lineheight(40px, 50px);
}
@include media-breakpoint-up(lg) {
@include fontsize-lineheight(60px, 72px);
}
}
// ....and so on
This still looks quite repetitive, so I made a map containing lists for the font sizes and line heights, like this:
// font-sizes, from left to right:
// lg md sm
$heading-sizes: (
h1: 72px 48px 36px,
h2: 48px 32px 30px,
...
);
$heading-lineheights: (
h1: 72px 48px 36px,
h2: 48px 36px 30px,
...
);
Every entry in the map can be accessed with map-get
, for example:
$font-size-h1: map-get($heading-sizes, h1);
And every entry in a list can be accessed by its index (1-based) using nth
, for example:
$font-sizes-h1: map-get($heading-sizes, h1);
$font-size-h1-md: nth($font-sizes-h1, 2);
So all of the above can be combined into the following mixin:
@mixin heading($heading) {
$sizes: map-get($heading-sizes, $heading);
$line-heights: map-get($heading-lineheights, $heading);
@include fontsize-lineheight(nth($sizes, 3), nth($line-heights, 3));
@include media-breakpoint-up(md) {
@include fontsize-lineheight(nth($sizes, 2), nth($line-heights, 2));
}
@include media-breakpoint-up(lg) {
@include fontsize-lineheight(nth($sizes, 1), nth($line-heights, 1));
}
}
So putting everything together the final result is:
@function strip-units($value) {
@return $value / ($value * 0 + 1);
}
@mixin fontsize-lineheight($font-size, $line-height, $base: 16px) {
$remsize: (strip-units($font-size) / strip-units($base));
font-size: #{$remsize}rem;
line-height: strip-units($line-height / $font-size);
}
@mixin heading($heading) {
$sizes: map-get($heading-sizes, $heading);
$line-heights: map-get($heading-lineheights, $heading);
@include fontsize-lineheight(nth($sizes, 3), nth($line-heights, 3));
@include media-breakpoint-up(md) {
@include fontsize-lineheight(nth($sizes, 2), nth($line-heights, 2));
}
@include media-breakpoint-up(lg) {
@include fontsize-lineheight(nth($sizes, 1), nth($line-heights, 1));
}
}
$heading-sizes: (
h1: 72px 48px 36px,
h2: 48px 32px 30px,
h3: 36px 30px 24px,
h4: 30px 24px 20px,
h5: 24px 20px 18px,
h6: 20px 18px 16px
);
$heading-lineheights: (
h1: 72px 48px 36px,
h2: 48px 36px 30px,
h3: 42px 36px 30px,
h4: 36px 30px 24px,
h5: 30px 24px 21px,
h6: 24px 21px 18px
);
$headings: h1 h2 h3 h4 h5 h6;
@each $heading in $headings {
#{$heading}, .#{$heading} {
@include heading($heading);
}
}
If you have any reactions please leave them in the comments section below!