Create CSS classes dynamically
SASS interpolation is an useful tool that comes in handy when we have to create CSS classes name dynamically from an array.
For the first version of this blog, I wanted different gradients based on post tag (git, vsc, nodejs...). Each tag is append to the main card class in order to create a modifier
// card.njk
<article class="c-card c-card--{{ tag }}"> ... </div>
and its CSS was this
// card.scss
$co_card-gradient-git: #eb3349, #f45c43;
$co_card-gradient-vsc: #1a2980, #26d0ce;
$co_card-gradient-nodejs: #36582f, #7ab659;
.c-card--git {
background: linear-gradient(to right, $co_card-gradient-git);
}
.c-card--vsc {
background: linear-gradient(to right, $co_card-gradient-vsc);
}
.c-card--nodejs {
background: linear-gradient(to right, $co_card-gradient-nodejs);
}
In attempt to remove repetitions and have a DRY (Don't Repeat Yourself) code, I tried to loop through a SASS list and generate classes
$tags: git, vsc, nodejs;
@each $tag in $tags {
.c-card--#{$tag} {
background: linear-gradient(to right, $co_card-gradient-#{tag});
}
}
but unfortunately SASS does not support variable name interpolation at the moment! ๐
If you try to compile the snippet above, you will get
Sass Error: Undefined variable: "$co_card-gradient-"
Read more about sass interpolation
The workaround
- create a
mixin
using SASS conditionals with all cases you need
@mixin card-gradient($tag) {
@if $tag == 'git' {
background: linear-gradient($co_card-gradient-git);
}
@else if $tag == 'vsc' {
background: linear-gradient($co_card-gradient-vsc);
}
@else if $tag == 'nodejs' {
background: linear-gradient($co_card-gradient-nodejs);
}
}
- use the tag list to dynamically generate classes names
$tags: git, vsc, nodejs;
@each $tag in $tags {
.c-card--#{$tag} {
@include card-gradient($tag);
}
}
Dynamic classes using CSS vars
Note that if we use CSS vars instead of SASS vars we can interpolate the class name with #{}
syntax.
- Declare the CSS var
:root {
--co_palette-1: #ef476f;
/* ... */
}
- Use the interpolation where it is called, for example in a
@for
loop
@for $i from 1 through 5 {
.t-palette-color--#{$i} {
background-color: var(--co_palette-#{$i});
}
}
If we need to mantain SASS vars (in my code, I needed to use darken
and lighten
SASS function) we could interpolate classes name like this:
$co_palette-1: #ef476f;
$co_palette-2: #ffc233;
$co_palette-3: #06d6a0;
$co_palette-4: #1b98e0;
$co_palette-5: #ff9f1c;
:root {
--co_palette-1: #{$co_palette-1};
--co_palette-2: #{$co_palette-2};
--co_palette-3: #{$co_palette-3};
--co_palette-4: #{$co_palette-4};
--co_palette-5: #{$co_palette-5};
--co_palette-1--lighten: #{lighten($co_palette-1, 10%)};
--co_palette-2--lighten: #{lighten($co_palette-2, 10%)};
--co_palette-3--lighten: #{lighten($co_palette-3, 10%)};
--co_palette-4--lighten: #{lighten($co_palette-4, 10%)};
--co_palette-5--lighten: #{lighten($co_palette-5, 10%)};
}
@for $i from 1 through 5 {
.t-palette-color--#{$i} .c-card::before {
background-color: var(--co_palette-#{$i});
color: var(--co_palette-#{$i}--lighten);
}
}
Output:
.t-palette-color--1 .c-card::before {
background-color: var(--co_palette-1);
color: var(--co_palette-1--lighten);
}
.t-palette-color--2 .c-card::before {
background-color: var(--co_palette-2);
color: var(--co_palette-2--lighten);
}
.t-palette-color--3 .c-card::before {
background-color: var(--co_palette-3);
color: var(--co_palette-3--lighten);
}
.t-palette-color--4 .c-card::before {
background-color: var(--co_palette-4);
color: var(--co_palette-4--lighten);
}
.t-palette-color--5 .c-card::before {
background-color: var(--co_palette-5);
color: var(--co_palette-5--lighten);
}