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;
}
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)" />
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;
Then, we need to identify the step of column (width of one column):
$colStep: 100% / $colsAmount
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;
}
}
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%;
}
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;
For comfortable binding of our property with some shorthand key, we need to use scss map structure:
$gaps: ('m': 'margin', 'p': 'padding');
Then goes directions of gaps and amount of steps we need to have:
$directions: (
'l': 'left',
'r': 'right',
't': 'top',
'b': 'bottom',
);
$stepsAmount: 12;
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;
}
}
}
}
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;
}
}
}
}
}
All the code examples you may find in my Github gists
Top comments (0)