How Do PostCSS + Tailwind Work? Part 1: Shrinking the Build
Tags: postcss, tailwindcss
Published: 2020-11-25 13:54:55

---
title: How Do PostCSS + Tailwind Work? Part 1: Shrinking the Build
published: true
series: How Do PostCSS + Tailwind Work? 
description: Getting to a basic understanding of what Tailwind CSS, and PostCSS plugins like it, actually work.
tags: postCSS, tailwind
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/v9u7s7wso1ros9o8k9g7.jpg
---

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](https://tailwindui.com/components) 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][2]: 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.
> -- <cite>[It's Time for Everyone to Learn About PostCSS][1], David Clark</cite>

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:

```bash
$ postcss --use <plugin-name> [-c options.json] -o main.css css/*.css
```

First things first:

```bash
$ 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:

```css
/* hi.css */

@tailwind base;
@tailwind components;
@tailwind utilities;
```

That's from [the 'install' docs][2] of Tailwind. As you might guess, `@tailwind` is not valid CSS, so I'm thinking this will get pre-processed when I do

```bash
$ 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"][3], they call it). 

## Experiment #2: `postcss.config.js`

I'm thinking that if I create this file (from [the Tailwind docs][1] again),

```javascript
// postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
  },
}
```

maybe I won't have to do `--use` when using the CLI?

```bash
$ 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][4] we have to make this alteration to get unused CSS purged:

```javascript
// 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:

```bash
$ 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:

```html
<!--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!).


[1]: https://davidtheclark.com/its-time-for-everyone-to-learn-about-postcss/#what-we-mean-when-we-say-postcss
[2]: https://tailwindcss.com/docs/installation#installing-tailwind-as-a-post-css-plugin
[3]: https://tailwindcss.com/docs/preflight
[4]: https://tailwindcss.com/docs/optimizing-for-production#removing-unused-css