Preface
Goal: Generate custom spacing classes step by step, utilizing postcss loop capability.
Even when utilizing CSS Framework,
theme making require to create custom CSS.
One of my need is spacing class, that is not available
in neither Bulma nor Materialize.
This spacing class can be made utilizing PostCSS
,
or to be exact, using PreCSS plugin
.
These guidance will save you a lot of coding time. And also tons of typing.
Reading
All code below are ported from the previous SASS article:
CSS Preprocessor
SASS/SCSS is one alternative among CSS preprocessor family.
Configuration
This assume you have read the previous articles. We are using this configuration:
module.exports = {
syntax: 'postcss-scss',
plugins: [
require('postcss-strip-inline-comments'),
require('postcss-each'),
require('precss'),
require('postcss-prettify'),
],
}
If you haven’t read the configuration guidance yet, I suggest you to take one step behind to this article:
0: Comment
Consider test our postcss
with gulp
to remove this simple comment.
PostCSS Source
// This line will be removed
.m-1 {
margin: 1px;
}
CSS Result
The result would be:
.m-1 {
margin: 1px;
}
1: @for
Imagine that we need a sequence of classes to handle margin, similar like CSS below:
.m-1 {
margin: 1px;
}
This can be achieved with this loop constructor
@for ... from ... to ... {
...
}
CSS Class Naming
We want to achieve this class naming
- .m-$(number)
PostCSS Source
For comfort reason, we also need to define intial value for the loop.
// variable initialization
$loop-begin: 1;
$loop-stop: 3;
// loop
@for $cursor from $loop-begin to $loop-stop {
.m-$(cursor) {
margin: $(cursor)px;
}
}
Variable Interpolation
Inside the loop we have this $(…) code. This is variable interpolation.
We use variable interpolation, when we want to extract value into the css.
CSS Result
The $(cursor) variable takes care the rest, so now we have this result below:
.m-1 {
margin: 1px;
}
.m-2 {
margin: 2px;
}
.m-3 {
margin: 3px;
}
Limitation
The issue with @for is we cannot use interval. We can have this [1, 2, 3, 4, 5] sequence. But we cannot have this [5, 10, 15, 20, 25] sequence.
2: @for by Interval
To solve [5, 10, 15, 20, 25] sequence, we can utilize @for … by.
@for ... from ... to ... by ... {
...
}
CSS Class Naming
We still want to achieve this class naming
- .m-$(number)
PostCSS Source
Consider this @for loop below:
// variable initialization
$loop-begin: 5;
$loop-stop: 25;
$interval: 5;
// loop
@for $cursor from $loop-begin
to $loop-stop by $interval {
.m-$(cursor) {
margin: $(cursor)px;
}
}
CSS Result
.m-5 {
margin: 5px;
}
.m-10 {
margin: 10px;
}
.m-15 {
margin: 15px;
}
.m-20 {
margin: 20px;
}
.m-25 {
margin: 25px;
}
3: @each
Before facing complex situation, consider a simple example.
Challenge
Now we have challenge that margin property has these variants:
-
margin,
-
margin-top,
-
margin-bottom,
-
margin-left,
-
margin-right
For simplicity reason, I exclude the original margin variant.
CSS Class Naming
We want to achieve this class naming
- .m-$(side)-5
List Declaration
PostCSS can handle a list.
(top, bottom, left, right)
Accessing List
This list can be accessed by using @each iterator.
@each ... in ... {
...
}
PostCSS Source
Consider accessing $sides by this example below:
@each $side in (top, bottom, left, right) {
.m-$(side)-5 {
margin-$(side): 5px;
}
}
CSS Result
Now we have this:
.m-top-5 {
margin-top: 5px;
}
.m-bottom-5 {
margin-bottom: 5px;
}
.m-left-5 {
margin-left: 5px;
}
.m-right-5 {
margin-right: 5px;
}
This is ugly, because we want .m-t-5, instead of .m-top-5.
4: @each with Pairs
PostCSS has solution for this issue.
CSS Class Naming
We want to achieve this class naming
- .m-$(abbreviation)-5
List Declaration
Luckily, PostCSS allow us to have pairs in list. Now we have property as pairs as shown below:
(top, bottom, left, right), (t, b, l, r)
PostCSS Source
Consider rewrite previous code.
// sides : properties, abbreviation;
@each $side, $name
in (top, bottom, left, right), (t, b, l, r) {
.m-$(name)-5 {
margin-$(side): 5px;
}
}
CSS Result
Now we have what wee need.
.m-t-5 {
margin-top: 5px;
}
.m-b-5 {
margin-bottom: 5px;
}
.m-l-5 {
margin-left: 5px;
}
.m-r-5 {
margin-right: 5px;
}
Notice that I use .m-t-5, instead of .mt-5, to differ from bootstrap spacing classes.
5: @for @each
Consider leverage to a more complex situation. We need a sequence of each classes.
CSS Class Naming
We want to achieve this class naming
- .m-$(subname)-$($number)
Skeleton
We put @each iterator inside @for loop.
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $side, $name
in (top, bottom, left, right), (t, b, l, r) {
...
}
}
PostCSS Source
// variable initialization
$loop-begin: 5;
$loop-stop: 25;
$interval: 5;
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $side, $name
// sides: properties, abbreviation;
in (top, bottom, left, right), (t, b, l, r) {
.m-$(name)-$(cursor) {
margin-$(side): $(cursor)px;
}
}
}
CSS Result
.m-t-5 {
margin-top: 5px;
}
.m-b-5 {
margin-bottom: 5px;
}
.m-l-5 {
margin-left: 5px;
}
.m-r-5 {
margin-right: 5px;
}
.m-t-10 {
margin-top: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.m-t-15 {
margin-top: 15px;
}
.m-b-15 {
margin-bottom: 15px;
}
.m-l-15 {
margin-left: 15px;
}
.m-r-15 {
margin-right: 15px;
}
.m-t-20 {
margin-top: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.m-t-25 {
margin-top: 25px;
}
.m-b-25 {
margin-bottom: 25px;
}
.m-l-25 {
margin-left: 25px;
}
.m-r-25 {
margin-right: 25px;
}
6: Nested @for
CSS Class Naming
We want to achieve this class naming
- .$(name)-$(subname)-$(number)
List Declaration
We define CSS property list:
- property: margin and padding
(margin, padding), (m, p)
- each has sub-property: top, bottom, left, right.
(top, bottom, left, right), (t, b, l, r)
Skeleton
We put both @each iterator, inside @for loop.
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $prop, $name
in (margin, padding), (m, p) {
@each $side, $subname
in (top, bottom, left, right), (t, b, l, r) {
...
}
}
}
PostCSS Source
// variable initialization
$loop-begin: 5;
$loop-stop: 25;
$interval: 5;
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $prop, $name
in (margin, padding), (m, p) {
@each $side, $subname
in (top, bottom, left, right), (t, b, l, r) {
.$(name)-$(subname)-$(cursor) {
$(prop)-$(side): $(cursor)px;
}
}
}
}
CSS Result
Now we have both margin and padding.
.m-t-5 {
margin-top: 5px;
}
.m-b-5 {
margin-bottom: 5px;
}
.m-l-5 {
margin-left: 5px;
}
.m-r-5 {
margin-right: 5px;
}
.p-t-5 {
padding-top: 5px;
}
.p-b-5 {
padding-bottom: 5px;
}
.p-l-5 {
padding-left: 5px;
}
.p-r-5 {
padding-right: 5px;
}
.m-t-10 {
margin-top: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.p-t-10 {
padding-top: 10px;
}
.p-b-10 {
padding-bottom: 10px;
}
.p-l-10 {
padding-left: 10px;
}
.p-r-10 {
padding-right: 10px;
}
.m-t-15 {
margin-top: 15px;
}
.m-b-15 {
margin-bottom: 15px;
}
.m-l-15 {
margin-left: 15px;
}
.m-r-15 {
margin-right: 15px;
}
.p-t-15 {
padding-top: 15px;
}
.p-b-15 {
padding-bottom: 15px;
}
.p-l-15 {
padding-left: 15px;
}
.p-r-15 {
padding-right: 15px;
}
.m-t-20 {
margin-top: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.p-t-20 {
padding-top: 20px;
}
.p-b-20 {
padding-bottom: 20px;
}
.p-l-20 {
padding-left: 20px;
}
.p-r-20 {
padding-right: 20px;
}
.m-t-25 {
margin-top: 25px;
}
.m-b-25 {
margin-bottom: 25px;
}
.m-l-25 {
margin-left: 25px;
}
.m-r-25 {
margin-right: 25px;
}
.p-t-25 {
padding-top: 25px;
}
.p-b-25 {
padding-bottom: 25px;
}
.p-l-25 {
padding-left: 25px;
}
.p-r-25 {
padding-right: 25px;
}
7: Final
Now comes the final result.
CSS Class Naming
We want to achieve both class naming
-
.$(name)-$(number)
-
.$(name)-$(subname)-$(number)
Skeleton
We put both @each iterator, inside @for loop.
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $prop, $name
in (margin, padding), (m, p) {
...
@each $side, $subname
in (top, bottom, left, right), (t, b, l, r) {
...
}
}
}
PostCSS Source
// variable initialization
$loop-begin: 5;
$loop-stop: 25;
$interval: 5;
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $prop, $name
in (margin, padding), (m, p) {
.$(name)-$(cursor) {
$(prop): $(cursor)px !important;
}
@each $side, $subname
in (top, bottom, left, right), (t, b, l, r) {
.$(name)-$(subname)-$(cursor) {
$(prop)-$(side): $(cursor)px !important;
}
}
}
}
Notice that I also put !important in css value.
CSS Result
.m-5 {
margin: 5px !important;
}
.m-t-5 {
margin-top: 5px !important;
}
.m-b-5 {
margin-bottom: 5px !important;
}
.m-l-5 {
margin-left: 5px !important;
}
.m-r-5 {
margin-right: 5px !important;
}
.p-5 {
padding: 5px !important;
}
.p-t-5 {
padding-top: 5px !important;
}
.p-b-5 {
padding-bottom: 5px !important;
}
.p-l-5 {
padding-left: 5px !important;
}
.p-r-5 {
padding-right: 5px !important;
}
.m-10 {
margin: 10px !important;
}
.m-t-10 {
margin-top: 10px !important;
}
.m-b-10 {
margin-bottom: 10px !important;
}
.m-l-10 {
margin-left: 10px !important;
}
.m-r-10 {
margin-right: 10px !important;
}
.p-10 {
padding: 10px !important;
}
.p-t-10 {
padding-top: 10px !important;
}
.p-b-10 {
padding-bottom: 10px !important;
}
.p-l-10 {
padding-left: 10px !important;
}
.p-r-10 {
padding-right: 10px !important;
}
.m-15 {
margin: 15px !important;
}
.m-t-15 {
margin-top: 15px !important;
}
.m-b-15 {
margin-bottom: 15px !important;
}
.m-l-15 {
margin-left: 15px !important;
}
.m-r-15 {
margin-right: 15px !important;
}
.p-15 {
padding: 15px !important;
}
.p-t-15 {
padding-top: 15px !important;
}
.p-b-15 {
padding-bottom: 15px !important;
}
.p-l-15 {
padding-left: 15px !important;
}
.p-r-15 {
padding-right: 15px !important;
}
.m-20 {
margin: 20px !important;
}
.m-t-20 {
margin-top: 20px !important;
}
.m-b-20 {
margin-bottom: 20px !important;
}
.m-l-20 {
margin-left: 20px !important;
}
.m-r-20 {
margin-right: 20px !important;
}
.p-20 {
padding: 20px !important;
}
.p-t-20 {
padding-top: 20px !important;
}
.p-b-20 {
padding-bottom: 20px !important;
}
.p-l-20 {
padding-left: 20px !important;
}
.p-r-20 {
padding-right: 20px !important;
}
.m-25 {
margin: 25px !important;
}
.m-t-25 {
margin-top: 25px !important;
}
.m-b-25 {
margin-bottom: 25px !important;
}
.m-l-25 {
margin-left: 25px !important;
}
.m-r-25 {
margin-right: 25px !important;
}
.p-25 {
padding: 25px !important;
}
.p-t-25 {
padding-top: 25px !important;
}
.p-b-25 {
padding-bottom: 25px !important;
}
.p-l-25 {
padding-left: 25px !important;
}
.p-r-25 {
padding-right: 25px !important;
}
Now you can use this spacing sass in your project.
8: Update: XY
For practical reason, I have to update this article.
-
Start form zero .0, instead of 5.
-
Consider X, and Y just like bootstrap.
.m-y-5 {
margin-top: 5px !important;
margin-bottom: 5px !important;
}
.m-x-5 {
margin-left: 5px !important;
margin-right: 5px !important;
}
The drawback is, I have bigger stylesheet size.
List Declaration
Luckily, sass allow us to have pairs in list. Now we have CSS sub-property as below:
(margin, padding), (m, p)
and
(top, bottom, left, right), (t, b, l, r)
and both below
(top, bottom)
(left, right)
And the loop is a little bit different:
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $prop, $name
in (margin, padding), (m, p) {
...
@each $side, $subname
in (top, bottom, left, right), (t, b, l, r) {
...
}
.$(name)-y-$(cursor) {
@each $side in (top, bottom) {
...
}
}
.$(name)-x-$(cursor) {
@each $side in (left, right) {
...
}
}
}
}
PostCSS Source
// variable initialization
$loop-begin: 0;
$loop-stop: 25;
$interval: 5;
@for $cursor from $loop-begin
to $loop-stop by $interval {
@each $prop, $name
in (margin, padding), (m, p) {
.$(name)-$(cursor) {
$(prop): $(cursor)px !important;
}
@each $side, $subname
in (top, bottom, left, right), (t, b, l, r) {
.$(name)-$(subname)-$(cursor) {
$(prop)-$(side): $(cursor)px !important;
}
}
.$(name)-y-$(cursor) {
@each $side in (top, bottom) {
$(prop)-$(side): $(cursor)px !important;
}
}
.$(name)-x-$(cursor) {
@each $side in (left, right) {
$(prop)-$(side): $(cursor)px !important;
}
}
}
}
CSS Result
These spacing classes is ready to serve.
What is Next ?
After this configuration
, it is a good time to examine postcss
in action.
Consider continue reading [ PostCSS - Loop - SugarSS ].
Thank you for reading.