· 5 min read

The Exciting PostCSS

This article was auto-translated from Chinese. Some nuances may be lost in translation.

From Sass to PostCSS

About a year ago, PostCSS started surging in popularity within the front-end ecosystem. The reasons were obvious: preprocessor-like features, highly customizable plugins, early access to cssnext capabilities, and effortless integration with build tools (Gulp, Webpack), making everyday development a breeze.

Variables

When I first encountered PostCSS, I was quite excited, but I soon caught myself wondering: “Is it really necessary to replace Sass right away?”

The advantage of PostCSS is that you can pick and choose only the plugins you need, using them on demand. Take variables, for example: postcss-simple-vars can simulate Sass variable declarations and usage. Yet to me, it still feels awkward. Beyond standard variable declarations, Sass provides data types like maps and lists, along with a comprehensive suite of API operations—such as retrieving values, conditionals, and loops.

$colors: (
  main: #abc,
  sub: #bac,
  word: #333,
);

.container {
  background-color: map-get($colors, $main);
  color: map-get($colors, word);
}

Even using CSS spec var() leads to the same limitation—there is no feature for querying maps or lists.

:root {
  --wordColor: #333;
  --bgColor: #fafafa;
}

body {
  background-color: var(--wordColor);
  color: var(--bgColor);
}

(Internal monologue: And writing it this way is kind of uglyreally ugly.)

Alternatively, Sass’s @function can wrap map-get even further:

$colors: (
  main: #abc,
  sub: #bac,
  word: #333,
);

/* alias method for getting color from $colors map

/// @param {$key} the key you want to choose

///

/// eg:

 color: c($word); 

*/

@function c($key) {
  @if map-has-key($colors, $key) {
    @return map-get($colors, $key);
  } @else {
    @error "Unknown key #{$key}";
  }
}

.container {
  background-color: map-get($colors, $main);

  color: map-get($colors, word);
}

Because the PostCSS ecosystem is vast and fragmented, many plugins from independent developers might be unmaintained or contain subtle bugs leading to compilation errors. By contrast, Sass’s built-in feature set is far more complete.

Mixins and Functions

The corresponding plugins are postcss-mixins and postcss-functions.

While they simulate mixin behavior, combining them with conditionals takes quite a bit of extra effort:


@mixin state($state,$namespace: '') {

  @if ($namespace != ''){

   .#{$namespace}-#{$state} {

     text-transform: uppercase;

   }

  }

  @else {

    .${state} {

      text-transform: uppercase;

    }

  }

}

The same goes for functions. If you write plain CSS paired with PostCSS, you can’t use Sass’s native functions. While defining custom functions in JavaScript is undeniably appealing, if you want to replicate Sass’s built-in functions, you end up having to reinvent the wheel, which becomes rather tedious.

Still Immature Compared to Sass

Compared to Sass, PostCSS is still a relatively young tool. Although its ecosystem is broad and plugins are abundant, versions are still changing rapidly, and many issues remain unresolved. Because the original Sass was written in Ruby, its performance is slower than PostCSS (okay, probably a lot slower), but its stability, well-rounded API, type system, and syntax are levels PostCSS has yet to reach.

The Advantages of PostCSS

Now, let’s talk about the advantages of PostCSS! Currently, my favorite tools to pair it with are Autoprefixer and cssnano.

Autoprefixer handles tedious CSS vendor prefixes for you. In the past, we resolved this with mixins; now, delegating it entirely to PostCSS produces much cleaner, simpler code. Meanwhile, cssnano takes care of CSS minification. Combined with Gulp, you can compile and minify your CSS simply by installing the necessary plugins.

Besides the plugins mentioned above, here are a few others I find great:

  • postcss-sorting: Sorts your CSS properties according to defined rules
  • precss: Bundles many Sass-like features
  • stylelint: Lints your CSS
  • stylefmt: Formats your CSS code according to stylelint rules
  • doiuse: Checks browser support for your CSS features
  • livereload: Under the power of Webpack, css-loader comes with hot reload pre-configured, applying newly modified styles instantly without a page refresh.

Why I’m Hesitant to Leave Sass

PostCSS is undeniably convenient, but running both together doesn’t necessarily streamline day-to-day development. Whenever an error occurs, you have to spend time debugging the underlying pipeline. It’s possible that PostCSS outputs code that causes Sass compilation to throw errors, or a plugin bug might prevent a file from compiling fully, causing chunks of CSS to silently fail. These are all real risks. In my current workflow, I only use autoprefixer, cssnano, and stylelint to help simplify and inspect my CSS.

Conclusion

Perhaps PostCSS will eventually surpass Sass completely. However, Sass’s mature, well-architected syntax is the main reason I’m hesitant to go all-in on PostCSS just yet. When PostCSS reaches the point where it can stand fully independent of Sass, I might make the switch—just like when I first learned CSS and hesitated for a long time before finally picking up Sass. That said, the two (Sass and PostCSS) can certainly coexist and complement each other.

While enjoying the extreme flexibility of PostCSS, we must also be mindful of leaky abstractions—plugins may break over time as specifications evolve or when authors abandon maintenance. Individually, these plugins may seem trivial, but collectively they save a massive amount of time. On the other hand, while Sass’s comprehensive syntax and variable system require an initial learning curve, they offer a unified, consistent syntax that dramatically improves CSS maintainability.

In this era where componentization thrives, front-end development is shifting toward maintaining modular files (React, CSS Modules, etc.). In the end, we might not even need such complex machinery (referring to Sass functions, variables, etc.) and may return to plain, vanilla CSS after all.

References

Related Posts

Explore Other Topics