PostCSS: Shiny CSS PreProcessor written in JavaScript ?

PostCSS will let you process the CSS with JavaScript & AST. It takes PreProcessing CSS to new level. You can include it as processor in Webpack or Rollup

PostCSS: Shiny CSS PreProcessor written in JavaScript ?

Managing CSS is an ongoing struggle. There are many ongoing debates trying to justify one or other way to manage CSS. PostCSS takes PreProcessing CSS to another level.

What is it?

PostCSS will let you process the CSS. That's it. It is that simple.

It is written in JavaScript to fit the need of having a node based toolchain for Front End Workflow.

How does it work?

PostCSS will take CSS styles as input and build an AST Tree. AST is Abstract Syntax Tree; which will let you operate on the CSS code.

Now with AST, possibilities are endless. Why so? It is like having Regular Expressions for String operations like matching, replacing etc.

For a small set, it seems trivial but when the size of set increases, the problem increases exponentially as well. This increase introduces a lot of effort to be just handled manually.

For Example:

#main {
  border: 1px solid black;
}
{
  "raws": {
    "semicolon": false,
    "after": ""
  },
  "type": "root",
  "nodes": [
    {
      "raws": {
        "before": "",
        "between": " ",
        "semicolon": true,
        "after": "\n"
      },
      "type": "rule",
      "nodes": [
        {
          "raws": {
            "before": "\n  ",
            "between": ": "
          },
          "type": "decl",
          "source": {
            "start": {
              "line": 2,
              "column": 3
            },
            "input": {
              "css": "#main {\n  border: 1px solid black;\n}",
              "id": "<input css 11>"
            },
            "end": {
              "line": 2,
              "column": 26
            }
          },
          "prop": "border",
          "value": "1px solid black"
        }
      ],
      "source": {
        "start": {
          "line": 1,
          "column": 1
        },
        "input": {
          "css": "#main {\n  border: 1px solid black;\n}",
          "id": "<input css 11>"
        },
        "end": {
          "line": 3,
          "column": 1
        }
      },
      "selector": "#main"
    }
  ],
  "source": {
    "input": {
      "css": "#main {\n  border: 1px solid black;\n}",
      "id": "<input css 11>"
    },
    "start": {
      "line": 1,
      "column": 1
    }
  }
}

Check and experiment more with AST on  https://astexplorer.net/#/gist/5c9fb834b3517b2480d6510850e2aeb4/latest

But generating AST is only the first step to start processing the CSS.

For example, you have a rule border: 1px solid #000000; and you wanna just replace all occurrences of #000000 with black.

Of course it is not a perfect example, it should be otherwise, though for learning purpose, let's try to achieve this in a PostCSS plugin.
#main {
  border: 1px solid #000000;
}
#main {
  border: 1px solid black;
}

To achieve the above,

import * as postcss from 'postcss';

export default postcss.plugin('postcss-replace-black', (options = {}) => {
  // Work with options here
  return root => {
    // Transform CSS AST here
    root.walkRules(rule => {
      // Transform each rule here
      rule.walkDecls(decl => {
        // Transform each property declaration here
        if (decl.value.match('#000000')) {
          decl.value = decl.value.replace('#000000', 'black');
        }
      });
    });
  };
});

Now that was very simple plugin, there are many already made plugins which you can use to complete your task. Plugins like autoprefixer to auto prefix the new browser specific features like once upon a time flex was not adopted completely as specification.

Anyways you can read a small intro about flex here: https://time2hack.com/2017/09/css-css3-flexbox-layout/

PostCSS integrates well with bundlers like Webpack, Rollup etc. For example:

Webpack:

const postcssReplaceBlack = require('postcss-replace-black');
const autoprefixer = require('autoprefixer');

module.exports = {
  entry: './src/index.js',
  output: {
    path: './dist',
    filename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /\.css?$/,
        use: [
          'css-loader',
          'style-loader', 
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [
                postcssReplaceBlack(),
                autoprefixer()
              ]
            }
          },
        ],
      }
      ...
    ],
  }
}
...

The above loaders are available as their exact names on npm

npm i -D postcss-loader css-loader style-loader autoprefixer

Rollup:

import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer'
import postcssReplaceBlack from 'postcss-replace-black';

export default {
  input: 'src/index.js',
  output: {
	file: 'public/bundle.js',
	format: 'iife',
	sourcemap: true
  },
  plugins: [
    postcss({
      plugins: [
        postcssReplaceBlack(),
        autoprefixer()
      ]
    })
  ]
};

The above loaders are available as their exact names on npm

npm i -D rollup-plugin-postcss autoprefixer

More Plugins ?

You can search for useful and interesting PostCSS plugins here: https://www.postcss.parts/


Please share this with others and let us know your feedback in comments.