How to generate Static Atomic CSS with SCSS

disgustingdev

disgusting-dev

Posted on April 5, 2021

How to generate Static Atomic CSS with SCSS

In this article, I'd like to briefly show and explain you some snippets of SCSS-code responsible for generation of step-based classes

What is Static Atomic CSS?

Atomic CSS is one of popular CSS Methodologies, that says 'each css property has its own dedicated class' - for example

.bg-red {
   background: red;
}
Enter fullscreen mode Exit fullscreen mode

The ideas behind Atomic were about:

  • Swapping code complexity from CSS to HTML/JSX markup and to not be looking to couple files at the same time, trying to find needed class for your markup
  • Preventing CSS classes from being mutable and leading to different sorts of bugs

Types of Atomic

There are two types of how you'd write atomic classes:

  • Static - this is about the example above - you've wrote a class and now it exists in your app without some modifications from any side
  • Dynamic - when every class looks like a function so the system creates needed class at build time:
<div class="bg(red)" />
Enter fullscreen mode Exit fullscreen mode

As Dynamic classes will be transformed to static anyway, I've decided to concentrate only on static part.

Here I'll show an example that might be useful when your design system requires some custom behaviour:

Grid generation

This comes from bootstrap approach: When you need to define some grid system, bootstrap proposes you classes from .col-1 to col-12

Let's say that your Design Team specified they need to have 9 columns, each column should have padding of 5px on vertical etc (any strange requirement you could imagine)

On one hand, Front End Dev can always tell "I just won't do this cause it smells like a bad pattern". On the other hand, it still might be interesting, how to do such thing

For such ideas, we have CSS pre/post-processors (In the article SCSS was used, but it can be actually any processor that you wanna use). They propose us more coding solutions rather than repetative writing of css without some logic behind

And I agree this is not exactly Atomic class example, but it close to paradigm I'm gonna show in next example

Implementation

So, for grid generation, we need to define our amount of columns - let's say it is 9:

$colsAmount: 9;
Enter fullscreen mode Exit fullscreen mode

Then, we need to identify the step of column (width of one column):

$colStep: 100% / $colsAmount
Enter fullscreen mode Exit fullscreen mode

And now we just need to use for loop and generate needed amount of classes:

$colsAmount: 9;
$colStep: 100% / $colsAmount;

@for $i from 1 through $colsAmount {
  .col-#{$i} {
    width: $colStep * $i;
  }
}
Enter fullscreen mode Exit fullscreen mode

The output in pure CSS will be the next:

.col-1 {
  width: 11.1111111111%;
}

.col-2 {
  width: 22.2222222222%;
}

.col-3 {
  width: 33.3333333333%;
}

.col-4 {
  width: 44.4444444444%;
}

.col-5 {
  width: 55.5555555556%;
}

.col-6 {
  width: 66.6666666667%;
}

.col-7 {
  width: 77.7777777778%;
}

.col-8 {
  width: 88.8888888889%;
}

.col-9 {
  width: 100%;
}
Enter fullscreen mode Exit fullscreen mode

And as you already got, inside of this loop generation you may type any logic you need and get preferred behaviour

Margin-padding

Let's now move to example, where we need to generate some defined step for our paddings and margins.

Here I should ask you guys to forgive me - cause I'm going to show you 3 level of loop with if statements (of course my nickname assumes that I will write exactly the code of such quality, but that's not what it really means :))

$basicStep: 4px;
Enter fullscreen mode Exit fullscreen mode

For comfortable binding of our property with some shorthand key, we need to use scss map structure:

$gaps: ('m': 'margin', 'p': 'padding');
Enter fullscreen mode Exit fullscreen mode

Then goes directions of gaps and amount of steps we need to have:

$directions: (
  'l': 'left',
  'r': 'right',
  't': 'top',
  'b': 'bottom',
);

$stepsAmount: 12;
Enter fullscreen mode Exit fullscreen mode

And now we need:

  • go through map of gaps
  • for each gap, create class with each direction and each step
@each $gapKey, $gapValue in $gaps {
  @each $dirKey, $dirValue in $directions {
    @for $i from 1 through 12 {
      .#{$gapKey}#{$dirKey}-#{$i} {
        #{$gapValue}-#{$dirValue}: $i * $basicStep;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

It will generate you 300 rows of classes from .ml-1 to .pb-12

You promised if statements!

Here they are, we defined only for each side separately, but I want to use x (right-left), y (top-bottom) and a(all directions) as classes, so I need to cover them with ifs (since SCSS doesn't have switch/case solution)

$basicStep: 4px;
$stepsAmount: 12;

$gaps: ('m': 'margin', 'p': 'padding');
$directions: (
  'l': 'left',
  'r': 'right',
  't': 'top',
  'b': 'bottom',
  'a': 'all',
  'y': 'vertical',
  'x': 'horizontal'
);

@each $gapKey, $gapValue in $gaps {
  @each $dirKey, $dirValue in $directions {
    @for $i from 1 through 12 {
      @if $dirKey == 'a' {
        .#{$gapKey}#{$dirKey}-#{$i} {
          #{$gapValue}: $i * $basicStep;
        }
      }

      @else if $dirKey == 'x' {
        .#{$gapKey}#{$dirKey}-#{$i} {
          #{$gapValue}-right: $i * $basicStep;
          #{$gapValue}-left: $i * $basicStep;
        }
      }

      @else if $dirKey == 'y' {
        .#{$gapKey}#{$dirKey}-#{$i} {
          #{$gapValue}-top: $i * $basicStep;
          #{$gapValue}-bottom: $i * $basicStep;
        }
      }

      @else {
        .#{$gapKey}#{$dirKey}-#{$i} {
          #{$gapValue}-#{$dirValue}: $i * $basicStep;
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

All the code examples you may find in my Github gists

Thank you for your attention, see you next time!

💖 💪 🙅 🚩
disgustingdev
disgusting-dev

Posted on April 5, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related