A Selection Of PostCSS Plugins

This is an opinionated approach. And, the "assertions" I make are just my opinion.

I've only recently started using PostCSS, but I can say it's a breath of fresh air. In full-disclosure, my experience with Sass, Less, Stylus, or whatever, is limited. The main reason for this: I don't want to put out a cigarette with a fire-truck.

This brings me to the main characteristic of PostCSS that I find exceptionally powerful: The traditional "features" of CSS preprocessors are implemented through plugins; PostCSS, itself, is the foundation upon which plugins are built.

From a software engineering perspective, there are a lot of reasons the "plugin" approach is powerful. And, from an implementation perspective, PostCSS is an impressive accomplishment. But, that's not why I'm happy to be using PostCSS.

The reason I'm thrilled to be using PostCSS is because it is possible to limit the capabilities of the CSS preprocessing system used on a project.

Power#

CSS preprocessors are great because in writing the CSS for your project, you can write less. This can yield:

  • Less code to understand
  • Less code to maintain
  • A reduced chance of unknown dependencies; changing something is less-likely to break something else
  • Taking care of loose-ends is easier (see Autoprefixer)

These, and other benefits, are undeniable.

Limits are a good thing#

But, I've always had a major apprehension: In my opinion, for the most part, CSS preprocessors are too powerful. Generally speaking, everything they do is useful, but that doesn't mean it's not too much.

In a large system, inevitably, the problem always present, always lurking, is maintainability. A solution to a problem is great, but if it's not maintainable, is it really a solution? Or, is it actually irresponsible? Time-constraints are possibly the only valid reason for not writing a maintainable solution. I mean, I'm just rambling here, but whenever I'm writing something I try like hell to make it easy to understand and maintain. I really really try to do this.

And, to my thinking, the primary contributor to systems being unmaintainable is complexity.

It's not an original idea. There is a great paper on the subject (and certainly many others). The paper is an incredible read, and one of my favorite passages is:

Power corrupts What we mean by this is that, in the absence of language-enforced guarantees (i.e. restrictions on the power of the language) mistakes (and abuses) will happen. [...] The bottom line is that the more powerful a language (i.e. the more that is possible within the language), the harder it is to understand systems constructed in it.

-- Ben Moseley and Peter Marks, Out of the Tar Pit, 2006

Simplicity and the management of complexity are paramount to creating maintainable systems.

The power of the tools being used to create a system often undermine the maintainability of the system.

Restraint#

So, how does PostCSS relate to maintainability? And, with it being absurdly powerful and flexible, and power being corrupting and all, why am I excited about PostCSS?

PostCSS enables you to choose which capabilities to include and, more importantly, which to exclude.

If restraint is used when selecting PostCSS plugins, it can become much easier to write simple solutions, solutions that solve the problem but can also be easily understood and maintained. Maybe I'm wrong, but this seems a logical consequence of having less power than that provided by Sass, Less, Stylus, or even the plugin packs featured on the PostCSS github page. The ultimate "less power" is straight-up CSS. Which is unnecessary. It's about balance.

The situation is simple: exercise restraint when selecting plugins so restraint is less necessary when crafting solutions. Why? Because, then you need to be careful less often. Restraint in the "plugin-selection" phase makes certain mistakes, convolutions, or abuses, simply impossible.

I'm not a CSS guru or ninja or titan, but this just seems reasonable to me.

Plugin selection#

For the PostCSS configuration used on my main project, I've selected eight plugins. This list will inevitably change, grow, contract and maybe even be eradicated, but this is where I'm at right now.

autoprefixer#

No introduction necessary.

postcss-clearfix#

Because life should be simple. From the README:

.foo {
    clear: fix;
}

Becomes:

.foo:after{
    content: '';
    display: table;
    clear: both;
}

postcss-hexrgba#

Writing rgba(#e8e8e8, .2) is definitely more natural than the rgba(232, 232, 232, .2) equivalent. A runner up was postcss-color-hex-alpha which allows #e8e8e833 (and #rgba), but 33 doesn't translate to 20%, in my mind, as easily as 0.2 does.

From the README:

.bar {
    background: linear-gradient(rgba(#fff.1)rgba(#fff.2));
}

Becomes:

.bar {
    background: linear-gradient(rgba(255,255,255, .1)rgba(255,255,255, .2));
}

postcss-import#

Import is pretty critical. postcss-import was chosen over postcss-partial-import because, well, it has better documentation, I guess. Not really clear on how the latter differs from the former, and I attribute that to its documentation.

It has some nice features (searches various folders). I'll probably keep it pretty vanilla, though. The format is what you'd expect:

@import "css/foo.css";

postcss-nesting#

I considered not including this. I like to take the SuitCSS approach to naming conventions, which tends to limit the use of descendant and child selectors. But, being able to clearly "scope" certain styles to a given context, more clearly than just using descendent or child selectors, is pretty nice.

This was chosen over postcss-nested mostly because demanding the @nest at-rule causes the application of nesting to be more explicit. That's handy because I indent rule-sets for sub-components or modifiers.

From the README:

ab {
    color: red;
 
    @nest & c, & d {
        color: white;
    }
 
    @nest & & {
        color: blue;
    }
 
    @nest &:hover {
        color: black;
    }
 
    @media (min-width: 30em) {
        color: yellow;
    }
}

Becomes:

ab {
    color: red;
}
 
a c, a d, b c, b d {
    color: white;
}
 
a ab b {
    color: blue;
}
 
a:hoverb:hover {
    color: black;
}
 
@media (min-width: 30em) {
    ab {
        color: yellow;
    }
}

postcss-property-lookup#

I've never used this before. Looks awesome. The main benefit: dependencies / relations are explicit rather than implied.

From the README:

.Test {
    margin-left: 20px;
    margin-right: @margin-left;
    color: red;
    background: @color url('test.png');
}

Becomes:

.Test {
    margin-left: 20px;
    margin-right: 20px;
    color: red;
    background: red url('test.png');
}

postcss-simple-extend#

Extend is a given. But, postcss-simple-extend was chosen over postcss-extend because:

  • Having to clearly designate something as being intended for extending is a great thing.
  • Limiting a rule-set to only being extended, rather than possibly used directly and extended, is also a great thing.
  • With postcss-simple-extend, the end-result is intuitive. With postcss-extend (and Sass), the broad scope of what is actually done when @extending seems to make unintentionally creating rule-sets inevitable.

From the README:

@define-placeholder gigantic {
  font-size: 40em;
}
 
.foo {
  @extend gigantic;
  color: red;
}
 
.bar {
  @extend gigantic;
  color: orange;
}

Becomes:

.foo,
.bar {
  font-size: 40em;
}
 
.foo {
  color: red;
}
 
.bar {
  color: orange;
}

postcss-simple-vars#

Vars are crucial. postcss-css-variables looks to be a very nice alternative.

$blue: #056ef0;
$column: 200px;
 
.menu {
    width: calc(4 * $column);
}
.menu_link {
    background: $blue;
    width: $column;
}

Becomes:

.menu {
    width: calc(4 * 200px);
}
.menu_link {
    background: #056ef0;
    width: 200px;
}

Pretty Straight-forward#

That's the list. Not much to it.

Notably excluded:

  • Conditionals
  • Loops
  • Mixins
  • Custom properties

Time will tell whether this is a decent selection. I do have second thoughts about nesting...