According to the 2020 state of CSS report, Tailwind CSS stood out as one of the tools with the highest satisfaction ratio (86%). Tailwind has become a go-to solution to build web applications. And the reason is justified you can build and ship websites faster and still have 100% control over the styling unlike other CSS frameworks (Bulma, bootstrap, etc).
I was always curious about how tailwind generates 3645.2kB of CSS which is full of utility classes from "p-0 , mx-1 ... pt-40" to the color variants and all.
This blog covers how you can build TailwindCSS utility classes in a very minimal way and doesn't cover the plugin/config ecosystem it provides.
We will be building it with the help of Sass (SCSS) or syntactically awesome stylesheets. If you don't know what this is you can watch this video to get an overview. Using Sass will help us to write CSS code more efficiently and programmatic.
A short demo:
Browser doesn't understand what Sass actually is, so we need to compile it to CSS. We will use a bunch of tools to accomplish that. Now let's start by setting up our project.
yarn init -y
# or
npm init -y
This would create a minimal package.json
file for our project. Now we will add some devDependencies
yarn add -D autoprefixer node-sass postcss postcss-cli
# or
npm install autoprefixer node-sass postcss postcss-cli --save-dev
A brief explanation of what these actually do:
node-sass
helps in compiling.scss / .sass
files tocss
postcss
parser which tokenizes CSS code to create an abstract syntax tree, we will be using it's pluginsautoprefixer
plugin of postcss, adds vendor prefixes to CSS rule
Now, will add some scripts in package.json
, feel free to modify the input and output of your build.
"scripts": {
"build": "rm -rf build && yarn build-sass",
"build-sass": "node-sass --output-style expanded src/index.scss ./build/index.css && yarn build-autoprefix",
"build-autoprefix": "postcss --use autoprefixer --map --output ./build/index.css ./build/index.css"
}
build
clears build directory and runsbuild-sass
build-sass
compiles scss and build css in a new directory then runsbuild-autoprefix
build-autoprefix
adds vendor prefixes and generates source maps
Now let's create our input file src/index.scss
, if you write some valid scss
code and run yarn build
you should see the compiled css created.
Now that our project is setup we will look into creating text-color & background-color variants of tailwind. The base idea is to loop over all colors you have and generate utility classes (text-gray-100, bg-gray-100 ... etc).
Let's start by first creating a new directory utils
and a file inside it _colors.scss
. Adding a leading underscore in scss files is called partials this helps us in modularizing the code and importing it into other files.
To declare our colors we will use Maps which is provided by SASS to declare key-value pairs.
// utils/_colors.scss
$colors: (
'gray-100' #f7fafc,
'gray-200' #edf2f7,
'gray-300' #e2e8f0,
'gray-400' #cbd5e0,
'gray-500' #a0aec0,
'gray-600' #718096,
'gray-700' #4a5568,
'gray-800' #2d3748,
'gray-900' #1a202c
);
Now to loop over these we will use @each
rule this helps us to evaluate code for each element of a lists/maps to generate repetitive styles which is exactly what want to do.
// utils/_colors.scss
@each $name, $hex in $colors {
.text-#{$name} {
color: $hex;
}
.bg-#{$name} {
background-color: $hex;
}
}
Now let's import this partial in our input file
// src/index.scss
@import '../utils/colors';
On running yarn build
you should be able to see all your utility classes being generated.
/* build/index.css */
.text-gray-100 {
color: #f7fafc;
}
.bg-gray-100 {
background-color: #f7fafc;
}
.text-gray-200 {
color: #edf2f7;
}
.bg-gray-200 {
background-color: #edf2f7;
}
... and so on
Moving on to the next part we will look into creating margin , padding utility classes. We will be using lists data type in this case.
Let's start by creating a new partial _spacing.scss
and we will create 2 lists, 1st one would be $spaces
which will be used to calculate the spacing (For eg: m-4 -> margin: '1rem') and the 2nd one being $sides
which is basically the direction of spacing to create margin-(top, right, left, bottom) variants.
// utils/_spacing.scss
$spaces: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20;
$sides: 'top', 'right', 'bottom', 'left';
Now that we have our values all we have to do is now loop over them with the help of the @each
rule. To generate margin/padding-top,left,bottom,right utility classes we will nest another @each
rule to map over $sides
lists. We will also use str-slice
which returns the slice of string.
// utils/_spacing.scss
@each $space in $spaces {
.m-#{$space} {
margin: #{$space/4}rem;
}
.mx-#{$space} {
margin-left: #{$space/4}rem;
margin-right: #{$space/4}rem;
}
.my-#{$space} {
margin-top: #{$space/4}rem;
margin-bottom: #{$space/4}rem;
}
.px-#{$space} {
padding-left: #{$space/4}rem;
padding-right: #{$space/4}rem;
}
.py-#{$space} {
padding-top: #{$space/4}rem;
padding-bottom: #{$space/4}rem;
}
.p-#{$space} {
padding: #{$space/4}rem;
}
@each $side in $sides {
.m#{str-slice($side, 0, 1)}-#{$space} {
margin-#{$side}: #{$space/4}rem;
}
.p#{str-slice($side, 0, 1)}-#{$space} {
padding-#{$side}: #{$space/4}rem;
}
}
}
Make sure to import the partials in our input file.
// src/index.scss
@import '../utils/colors' , '../utils/spacing';
Now if you build it you should see the spacing variants generated.
/* build/index.css */
.m-0 {
margin: 0rem;
}
.p-0 {
padding: 0rem;
}
.mx-0 {
margin-left: 0rem;
margin-right: 0rem;
}
.my-0 {
margin-top: 0rem;
margin-bottom: 0rem;
}
... and so on
Using the same approach you can create breakpoints, typography, literally any kind of utility classes you want! I hope you found this article informative I thought this was cool enough implementation to share with you folks.
You can refer the code in this Github repo.
Let me know what you think about it, feel free to connect with me on Twitter if you have any questions. Cheers!