How Do PostCSS + Tailwind Work? Part 1: Shrinking the Build
Zev Averbach
Posted on November 25, 2020
I love when a library or utility has an intuitive API that you can just hit the ground running with, or when it has a massive audience so you can Stack Overflow your way to success. However, at least in web land things can be complex and counterintuitive enough that I'd like surpass "well-crafted StackOverflow search" familiarity with them.
One such technology is Tailwind CSS. I don't fancy myself a designer, and to be honest I've never gone deep with CSS: To be more honest, my best trick is "float: right;" which usually sends me down an increasingly fiddly path. I love Tailwind UI because everything looks so modern right out of the box, so I don't have to fiddle very much.
However, the class lists in Tailwind UI's components are necessarily verbose, and I'm eager to start using Tailwind CSS the recommended way: By building (compiling?) pre-processing my Tailwind-using projects' CSS. My understanding is that this 1) reduces the build size because it only includes the Tailwind classes you use, and 2) allows you to make your components/HTML readable again by abstracting (@apply
-ing) many class names into one.
For this post I'm going to focus on reducing the build size of the CSS.
Foundations
Tailwind is a PostCSS plugin, it turns out. The docs point in every direction but its npm page, which puzzled me at first: The reason for this is that it's a tool — a parser — which
...parses CSS into an abstract syntax tree (AST); passes that AST through any number of “plugin” functions; and then converts that AST back into a string, which you can output to a file.
-- It's Time for Everyone to Learn About PostCSS, David Clark
Thus, the plugins are where the action happens.
Experiment #1: postcss-cli
Both the PostCSS and Tailwind docs start immediately giving pointers about how to set yourself up in various bundlers. However, I'd like to hold off on that if possible and see if I can run the Tailwind plugin on its own. A brief, down-the-page section in the PostCSS README mentions postcss-cli
which can be used like so:
$ postcss --use <plugin-name> [-c options.json] -o main.css css/*.css
First things first:
$ npm install postcss postcss-cli tailwindcss
I don't quite know what sort of content to feed into the Tailwind plugin, but let's try the bare minimum:
/* hi.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
That's from the 'install' docs of Tailwind. As you might guess, @tailwind
is not valid CSS, so I'm thinking this will get pre-processed when I do
$ postcss --use tailwindcss -o main.css hi.css
Things are not working as expected on my Mac — it can't find the CLI to invoke it — but I Googled my way to npx postcss --use ...
, which does work. Oh, technology.
What this produces is what I assume is the entire codebase of those three "modules" referred to in hi.css
(fun fact, @tailwind <thing>
is called an "At-rule").
It's nearly 172,000 lines long! And indeed, from experimenting it looks like, as of today "base" is about 540 lines of code, "components" is 220, and "utilities" is 171,000!
It looks like "components" is dedicated exclusively to the class name "container" on various screen sizes and with different size prefixes. "base" doesn't define any classes, just sets some defaults for every conceivable HTML tag ("Preflight", they call it).
Experiment #2: postcss.config.js
I'm thinking that if I create this file (from the Tailwind docs again),
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
},
}
maybe I won't have to do --use
when using the CLI?
$ postcss -o main.css hi.css
Yes! This prevents the need for specifying which plugin(s) to invoke.
Shrink the Build
Going back to those 172,000 lines of 'rendered' Tailwind CSS code: Let's get Tailwind's help in whittling them down.
First stop, $ npx tailwindcss-cli@latest init
which creates a tailwind.config.js
. According to the docs we have to make this alteration to get unused CSS purged:
// tailwind.config.js
module.exports = {
purge: [
'./src/**/*.html',
'./src/**/*.svelte',
],
theme: {},
variants: {},
plugins: [],
}
You can add *.jsx
, etc. if you're working with other frameworks.
Experiment #3: How Small Will It Get?
First let's run the purge with no HTML/Svelte files whatsoever:
$ export NODE_ENV=production && npx tailwindcss-cli@latest build src/hi.css -o build/main.css
Woot! This produces a main.css
of only about 370 lines, which seems to be a subset of @reset
.
Experiment #4: Adding One Line To The Build
Now I'll add an index.html that uses one Tailwind class:
<!--src/index.html-->
<div class="bg-blue-400"></div>
Running build
produces another compact main.css
, but this time it seems to include all of @base
as well as .bg-blue-400
. main.css
is only 11kb, whereas the non-purged version from the beginning is 3.5mb!
Next Time
In the next post we'll explore how to customize and extract classes in Tailwind, as well as integrating it with a bundler (probably Snowpack!).
Posted on November 25, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 19, 2023