PostCSS — beyond the Autoprefixer

Develoger
Develoger
Published in
4 min readJan 29, 2018

--

Make sure you follow Andrey Sitnik (creator of PostCSS)

Hype that needed some time to happen

I bet that when you think about PostCSS you must think something like…

Yet the truth is that PostCSS is around for almost as long as React!

Initial commit

Why PostCSS?

The widest spread application of PostCSS usage is through seamless integration of Autoprefixer (Just one of many PostCSS plugins. Not lib on its own.)

display: flex;// becomesdisplay: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;

Writing such logic outside of more traditional SASS / LESS mixins and automatically making it part of your app build is just genius.

This sure seems awesome and is the main reason why this particular plugin has 11,103,982 downloads per month on NPM.

It’s obvious that industry needs a concept which PostCSS introduced.
The single PostCSS plugin has more downloads than the most popular JavaScript frameworks / libraries… Check it out on NPM trends.

PostCSS is not coupled with any specific framework / library. It’s a Front-End tool.

Babel for CSS

With plugins or better say set of plugins PostCSS can be used in the same way we use Babel in JavaScript.

CSSnext is set of plugins which among other awesome stuff can transform CSS custom properties into what browsers can interpret today.

Alternative to CSSnext (possibly better maintained) is postcss-preset-env

:root {
--fontSize: 1rem;
}

h1 {
font-size: var(--fontSize);
}

This will obviously fail to work in most browsers but with PostCSS it will be transformed into.

h1 {
font-size: 1rem;
}

PostCSS is “just” a parser for CSS. This means that with its API one can do anything possible.

Stylelint is PostCSS based! Check this awesome CSS linter here.

How PostCSS?

Weirdly enough the PostCSS is not about the CSS it’s about what JavaScript can do with your CSS.

With that being said, let’s do a simple transform of single display: flex to multiple browser-specific definitions.

// require the postcss for our plugin to work
const postcss = require('postcss');
// export the plugin
module.exports = postcss.plugin('postcss-flex', (opts) => {
opts = opts || {};
return (css) => {
// define the new values
const values = [
'-webkit-box',
'-webkit-flex',
'-ms-flexbox',
];
// iterate through declarations
css.walkDecls('display', decl => {
// ignore everything else other than flex
if (decl.value !== 'flex') return;
// map values to actual string
const newDecls = values.map((value) => {
return ` display: ${value}`;
}).join(';');
// add new declarations before the current
decl.before(newDecls);
});
};
});

Essentially PostCSS takes your CSS (or SASS / LESS) source as input, gives you the possibility to alter it and outputs the end result. Simple as that.

For more details regarding PostCSS API please go here. But beware that docs are automatically generated from JSdoc (If anyone ever ask you why you should use JSdocs, just show him/her PostCSS API docs). Even though they are insightful, IMHO it would be better if they were written manually.

Theming

If your theme support requires different palette set for each theme. Together with possibly some other variables which needs to be adjusted eg. font-sizes or border-radius or … One of the easiest (possibly best) ways to achieve this would be to separate mapping of the variables eg. colors, border-radius … in the theme files.

For this scenario, we can @import just the default theme file and replace the import to theme specific file during the build, via PostCSS. As result of this, we can build theme specific CSS bundles easily and never put any references to it in our SASS codebase.

@import './variables';.button--color-primary {
background-color: $primary;
}

Where variables.scss have such mapping.

@import '~styles/colors/colors';$primary: $green;

Theme specific variables.blue.scss have different mapping.

@import '~styles/colors/colors';$primary: $blue;

SASS itself does not support dynamic imports (imports with variables in their path). Issue #279 on Github of SASS
So doing this is practically not possible if you use SASS. At least without PostCSS…

This small plugin is just one example of creative usage of PostCSS.
How does it work?

/* 
example of options
const opts = {
originalValue: './variables',
targetValue: './variables.blue'
};
*/
css.walkAtRules((node) => {
const isImport = (node.name === 'import');
const isOriginalValue = node.params.includes(opts.originalValue);

if (isImport && isOriginalValue) {
node.params = `"${opts.targetValue}";`;
}
});

Fun fact

Autoprefixer will eventually become obsolete…

Source

If you need assistance with the topic feel free to contact me on Twitter or place a comment below.

--

--