PostCSS is a super powerful tool that can help you craft your own CSS build pipeline. But it can be a lot to take in all at once. Here are a few quick steps to getting started with PostCSS.
For years and years and years I used Sass. I began almost immediately after learning CSS. It was a phenomenal extension to CSS.
CSS has evolved significantly, even in the recent years. Enough to the point that some of the core offerings of Sass (like variables) are now available in regular old CSS. It started to feel like I wasn't getting as much out of Sass as I had, at least beyond the ability to nest selectors and import files (both of which are super valuable).
Then I discovered PostCSS. PostCSS is a build tool for CSS. it lets you write CSS (sometimes valid, sometimes not) and then allows you to insert plugins that manipulate the CSS before ultimately presenting something that is valid and useful to the browsers you want to support for your project.
PostCSS is really all about plugins. All PostCSS provides is the build tooling. The power comes with hooking in plugins to that build process.
The community is absolutely full of plugins. And there are some good resources out there on finding the right plugin (see Resources.
I don't want to overwhelm you right out of the gate. We'll begin with just a few plugins that I have found vital to my productivity in working with PostCSS. They include:
@import
to concatenate local files, similar to how Sass works.With that, let's get started. We're going to only build a simple PostCSS pipeline. We're not going to worry about HTML or JavaScript. We won't end up with something that's ready for production. But you'll have a core set of code that you'll be able to take into your project.
Follow along, and feel free to reference the example code at any point.
If you have a project ready to go, great! If not, feel free to follow my steps to get started, with the following notes:
For Step 3, your .gitignore
should have at least the following:
.gitignore
node_modules/
dist/
For Step 4, these are our dependencies:
postcss
postcss-cli
postcss-import
postcss-nested
postcss-cssnext
cssnano
For Step 5, these are the scripts you should add to package.json
:
package.json
{
// ...
"scripts": {
"build": "NODE_ENV=production postcss src/index.css -o dist/styles.css",
"dev": "postcss src/index.css -o dist/styles.css -w"
}
}
Once everything is setup and installed, you can configure PostCSS. Do this by creating a postcss.config.js
file in the root of your project. In it we're going to specify the plugins we want to use:
postcss.config.js
module.exports = {
plugins: [
require("postcss-import"),
require("postcss-nested"),
require("postcss-cssnext"),
...(process.env.NODE_ENV === "production" ? [require("cssnano")] : []),
],
};
The order of these plugins is super important. You're specifying the order in which they will run. This matters when each one plays a specific role.
Note here that we're simply requiring the plugins we want to use in an exported object. The only one that looks a little weird is the last one — cssnano
. What that line is saying that if the environment variable NODE_ENV
is set to production
then we want to use cssnano
, which means we want to compress the output. If not, we'll leave it expanded.
Notice that in our package.json
scripts, we specified NODE_ENV=production
in the build
script, but not the dev
script. That's doing to be the difference. dev
will watch for changes and write the expanded output. build
will run once and compress the output.
Unlike many other CLI programs, PostCSS does not allow us to specify the input and output files in the config.
Instead, we have to use them as arguments when running the postcss
command. The scripts you added to package.json
takes care of that. It says our main source file will be src/index.css
and it will build a combined output file at dist/styles.css
.
Here's what my code looks like at this point. Note that I don't have a package-lock.json
because I manage my dependencies a little differently.
Let's just get this thing working. Add your source file at src/index.css
:
src/index.css
body: {
background: blue;
}
And then run the build:
$ npm run build
You should now see a file at dist/styles.css
with that rule by itself. Notice that the output is compressed because we ran build
and not dev
.
dist/styles.css
body {
background: #00f;
}
Here is the code at this point.
Now let's put our PostCSS plugins to work by adding some CSS that is not valid.
Adjust the main CSS file to look like this:
src/index.css
@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
@import "partials/base";
Notice that we're importing two things — first a font from Google, then a file in partials/base
. Add that partial CSS file next:
src/partials/base.css
:root {
--color-red: red;
--color-blue: blue;
--font-size: 1rem;
}
h1 {
font-size: calc(var(--font-size) * 2);
a {
color: var(--color-blue);
}
}
Here we are using CSS variables, nesting rules, and using a function (calc
) that isn't supported everywhere.
Instead of running build
this time, let's run dev
:
$ npm run dev
The command won't close. This is because it's watching for changes. But it likely still rewrote the dist/styles.css
file.
It should look something like this:
dist/styles.css
@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
h1 {
font-size: 32px;
font-size: 2rem;
}
h1 a {
color: blue;
}
Notice that our styles, colors, and variables we calculated for us, and now we have CSS that is super duper widely supported.
Now make another change and see what happens! You don't have to run another command. Your output file gets written automatically.
That's it! You did it!
This was just a quick tutorial to get you up and running. Where you go from here is totally up to you.
The one thing to make sure you do is have a little fun on your adventure! See you next time.
Here is the full code example (or run it in the playground below).
And here are a couple resources on plugins these authors prefer:
But if you have a need, there's probably a plugin for it. Do some googling. And if you can't find what you're looking for, you could always write your own plugin.