Well done is better than well said

Benjamin Franklin

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);
}
}

🖥 Codepen example

Dynamic classes using CSS vars #

Note that if we use CSS vars instead of SASS vars we can interpolate the class name with #{} syntax.

  1. Declare the CSS var
:root {
--co_palette-1: #ef476f;
/* ... */
}
  1. 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);
}
Edit this page on GitLab

Comment on DEV or Hashnode

Take me to the next post!