It's Time for Everyone to Learn About PostCSS What It Really Is; What It Really Does
A while ago, I wrote “I’m Excited About PostCSS, But I’m Scared to Leave Sass”. Since then, I have wholeheartedly embraced PostCSS (and left Sass, at least temporarily). I’ve been using PostCSS on large-scale projects, contributing to and authoring plugins, communicating with the maintainers to learn more about what’s possible; and that’s all gone swimmingly. Just swimmingly.
Meanwhile, the buzz around PostCSS has increased, provoking all kinds of reactions — curiosity, excitement, suspicion, confusion, weariness, bitterness, defensiveness, vitriol, exhiliration, flippant indifference, smug disdain, cheerful fist-pumping, and so on.
I have two points I’d like to contribute to the fuss:
- You don’t have to be afraid. The number of tools out there to process style-code is actually pretty small (or so it seems to me, who also writes JavaScript). The addition of more possibilities won’t hurt anybody or anything.
- Everybody who writes CSS should learn what PostCSS is — what it really is, and what it can be used for (not just what some choleric, reactive Tweeter said about it) — whether or not you end up using it right now. Because if you think PostCSS is simply an alternative to Sass and Less, you’re misinformed.
I don’t know what I can do to encourage #1 … Some blend of comforting words, coach-like encouragement, gentle prodding, and perspective? I’m probably not the best counselor in this case.
So I’ll skip to #2, where I may be able to help. Having worked with PostCSS for a little while now, I think I’ve learned some things about it that are worth sharing.
What we mean when we say “PostCSS”
With the word “PostCSS” we might alternately refer to two things:
- There is PostCSS, the tool itself — what you get when you run
npm install postcss
— and - The PostCSS plugin ecosystem powered by that tool.
The tool itself is a Node.js module that 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. Each function the AST passes through may or may not transform it; sourcemaps will be generated to keep track of any changes.
The AST provides a straightforward API that developers can use to write plugins. For example, you can cycle through each rule set in a file with css.eachRule()
, or each declaration in a rule with rule.eachDecl()
. You can get the selector of a rule with rule.selector
, or the name of an at-rule with atRule.name
. From these few examples you can see that the PostCSS API makes it pretty easy to work with CSS source code (much easier and more accurately than if you were to rely on regular expressions, like a chump).
That’s all that PostCSS currently does: it does not alter your CSS. A plugin might do that. Or maybe it won’t. PostCSS plugins can do pretty much whatever they want with the parsed CSS. One plugin could enable variables, or some other useful language extension. Another could change all of your a
s to k
s. Another could log a warning whenever you use an ID selector. Another could add Masonic ASCII art to the top of your stylesheets. Another could count all the times you use float
declarations. And so on, forever.
PostCSS can power an unlimited variety of plugins that read and manipulate your CSS. These plugins have no unifying agenda, except to solve problems.
Note that neither the tool itself nor the plugin ecosystem are direct analogues to Sass and Less. However: bundle up a set of related plugins that transform author-friendly stylesheets into browser-friendly CSS, and then you have something analogous to the good ol’ “preprocessors.”
Buuuut keep in mind that these “plugin packs” are just additional members of the ecosystem, like all the non-packaged plugins. No given plugin or plugin pack is or represents “PostCSS”: instead, we have a growing ecosystem that consists of many individual modules (powered by PostCSS).
A few implications of PostCSS modularity
☞ Attempts to maintain that PostCSS is (or should be) a “postprocessor”, as opposed to the “preprocessors” Sass and Less, are misguided.
Whatever your definitions of “postprocessor” and “preprocessor”, there will be PostCSS plugins that fall into both camps. According to most definitions, Autoprefixer is the iconic “postprocessor”; but there’s also stuff like postcss-each
, which is very “preprocessor”-y.
There are also plugins that don’t transform your CSS at all, like stylelint
and postcss-bem-linter
and list-selectors
.
If you want to maintain some pure distinction in your own build process, only using PostCSS plugins for what you think of as “postprocessing” — well that’s just fine: select your plugins with care.
Building on that …
☞ Attempts to tie “PostCSS” to specific syntax extensions or transformations are misguided.
PostCSS is a low-level module that facilitates the creation of other tools; and there are no limits on what those higher-level tools (the plugins) might do.
So PostCSS is not “about” allowing you write CSS Of The Future (syntax and functions from spec drafts) any more that it is “about” providing loops and conditionals and other Sass-like features. There are individual plugins that do both, and plugin packs that do both (cssnext and precss); but none of these represents the extent of PostCSS.
All this means that …
☞ When people think they are criticizing “PostCSS,” they are probably criticizing some particular plugin, or plugin pack, or particular way to use a particular plugin.
Which is fine — criticize away — but don’t trick yourself into dismissing other PostCSS-based tools because one of them rubs you the wrong way.
Which leads to the next point …
☞ You can opt into or out of any PostCSS plugin at any time.
Each plugin is only part of your build process because you put it there. If a plugin displeases you, remove it. Nobody is going to stop you.
Keep in mind, though, that some plugins can be used in a variety of ways, and maybe your displeasure can be appeased by using the plugin differently.
Maybe you, like Chris Eppstein, don’t like that with postcss-define-property
you can create new properties that look exactly like real, standard properties. Well, there’s a very simple solution for that: make new properties that don’t look exactly like real, standard properties.
And if you think a plugin needs better examples or new options, you can contribute, because …
☞ Plugins are relatively small modules, so they should be responsive to feedback and easy to contribute to.
And if you don’t see a plugin that does what you want, the way you want to do it …
☞ You can always build your own plugin to satisfy your own desires.
This is one of the most important points. It’s worth repeating …
☞ You can always build your own plugin to satisfy your own desires.
That makes PostCSS new and fantastic — the ease with which you can try something totally different.
Or you can slightly tweak what’s there. If some plugin uses syntax you like but functionality you hate, create a spin-off with the “right” functionality. If some other plugin provides functionality you love but syntax you abhor, create a spin-off with the “right” syntax. And when people see what you’ve wrought and complain about your plugin, you can always suggest they write their own, their way.
(At the risk of sounding ridiculous and grandiose …) I’d suggest that, for many designers and frontend developers, really learning about PostCSS should demystify the realm of CSS processing. All that functionality that Sass and Less provide — it’s not magic: it doesn’t have to be that way: those are just people behind the curtain, and though they may be smart and hard-working, you don’t have to assume that they know better than you what’s best for your situation.
Problem-Solving with PostCSS
Working with PostCSS has reminded me that CSS processing exists to solve problems; that pretty much all problems have multiple solutions; and that I might be qualified to pick between alternative solutions, or even construct my own.
Empowered by PostCSS, I’ve been tackling my CSS needs problem-first — similarly to how I deal with JavaScript. Instead of jumping behind a massive library before I really know what’s going on, first I think about the actual problem that needs solving; then I consider existing solutions; and then I either use an existing solution or start working on my own.
I think that process is fun and interesting.
Also, I think it’s helped me simplify my approach to CSS. Remember — though it may seem long ago — that many developers have at some point resisted adoption of Sass and Less because they were concerned that the “preprocessors” did not solve enough real problems to justify the complexity they might add to authoring. I’ve never really agreed (maybe because I’ve never minded a little complexity in my build process); but I do acknowledge the critique and concede that if you don’t feel like problems are getting solved by a tool, you should not feel obliged to use that tool.
I built (and continue to maintain) a substantial Sass utility library because it solved important problems at my previous job, where I had to pound out a lot of CSS quickly. Now I have a new job, with different problems (e.g. scalability, and weird, unique theming requirements); and for my current needs I find myself preferring a minimalistic approach to CSS, involving at least as much analysis as processing. I also want to limit the powers at my team’s disposal, only including non-standard features selectively. PostCSS, the tool and the ecosystem, is an excellent fit for my current needs.
That’s enough
I was going to write another section called “Now, for kicks, let’s address some ill-conceived criticisms of PostCSS.” But I think this post is already long enough. And I think that the astute reader will already understand what most of my rebuttals would look like. If you do have an accusation that you’d like to hear rebutted, tweet me at @davidtheclark and I’ll tweet you back.