Improving the Quality of Your CSS with PostCSS

Share this article

The term “code quality” is not new to programmers. After all, every developer knows that it’s not enough for the code to just work. It should also possess other qualities: it should be readable, well formatted and consistent. It should also match a certain standard of quantitative metrics. Unfortunately, this is often overlooked when writing CSS. We can spend a lot of time debating why this happens, but the important part is that CSS is code as much as JavaScript, PHP or anything else, and we should pay attention to the way we write it. Otherwise, it may cause things to be more complicated than they should be.

In this article, we will explore how we can utilise PostCSS to help us maintain a higher quality in our CSS code. First, let’s try to pinpoint what we actually mean by “better CSS code”. There are several things to watch out for:

  • Code should be consistent in style – You are free to choose how you name your classes, where you place new lines or how you list the properties, but you should do it in the same manner across all of your stylesheets. Consistent style improves readability and makes the code easier to understand.
  • Code should respect some metric standards – There are quantitative metrics that we can measure and keep at a certain threshold, such as the maximum precision of any selector or number of unique colours used on a page.
  • Hacks should be avoided – Certain constructs, such as, the !important directive may seem like a feasible solution at times, but they usually just make the code more complex.

Certainly this is not a complete list, but let’s focus on the above problems for now. They might seem obvious, but they can be easily overlooked when working on a large project with a big team where people can vary in skill. What we would like is a tool that can help us enforce these standards automatically by means of code analysis. This is where PostCSS comes in.

How Can PostCSS Help

In the previous article about PostCSS here at SitePoint, we covered what PostCSS is and how its plugins can be used for various purposes. In this article, we will focus on several PostCSS plugins that can help us keep our CSS code quality at its best.

Before we start, let’s set up a sandbox project we can experiment on. As opposed to using PostCSS via the command line as in the previous article, we’ll use Gulp instead. Start by creating a new folder and initiating an npm project there. After that we’ll need to install Gulp, the PostCSS plugin, and a reporter plugin for viewing the output of PostCSS plugins. To do that, navigate to the newly created project and run:

npm i gulp gulp-postcss postcss-reporter --save-dev

After that create an empty style.css file and a gulpfile.js with the following contents:

var gulp = require('gulp');
gulp.task('analyze-css', function () {
  var postcss = require('gulp-postcss');
  var reporter = require('postcss-reporter');

  return gulp.src('style.css')
    .pipe(postcss([ 
      reporter()
    ]));
});

This will define a task that will scan the contents of style.css and run it through a series of PostCSS plugins. You can already run gulp analyze-css but it won’t do much since there’s only a reporting plugin with nothing to report. Let’s add our first linting plugin.

Stylelint

By now there is surely a linter for any language and CSS is no exception. Stylelint allows you to validate your CSS code against a predefined set of rules which can check your code for consistent formatting, usage of certain rules, units or directives, as well as potential mistakes (such as incorrect colors). It allows you to define over a hundred rules — some of them do basic stuff, for instance, ensuring there is a space between a selector and the following curly brace, or that only single quotes are used. Others are more interesting. Here are a few examples:

  • property-blacklist and unit-blacklist allow you to specify a list of properties and units that cannot be used.
  • property-no-vendor-prefix warns you about using vendor prefixes for properties that don’t require them based on data from Can I use.
  • declaration-no-important disallows using the !important directive.
  • selector-max-specificity limits the maximum specificity of a selector.

Stylelint ships with all of the rules disabled by default, so you are expected to configure the rules yourselves. This can take a while, considering the amount of rules it has. Alternatively, you can use a predefined config such as stylelint-config-standard and extend it with your own rules.

Let’s set up stylelint with a standard rule set:

npm i stylelint stylelint-config-standard --save-dev

You’ll also need to extend your gulpfile to enable the new plugin:

var gulp = require('gulp');
gulp.task('analyze-css', function () {
  var postcss = require('gulp-postcss');
  var stylelint = require('stylelint');
  var reporter = require('postcss-reporter');

  return gulp.src('style.css')
    .pipe(postcss([
      stylelint(), 
      reporter()
    ]));
});

Stylelint rules can be configured inline in the gulpfile, but I prefer to keep them in a separate file. Create a .stylelintrc file in your project folder and add the following contents:

{
  "extends": "stylelint-config-standard"
}

This will tell stylelint that our own rule set will be based on the standard config. Now let’s update our style.css file and test the plugin against this savage piece of CSS:

.title,.content{ 
  background: #FFFFFF; 
  font-size:0.9em;


  margin: 0;
}

Just run gulp analyze-css and it will conveniently report a whole bunch of violations:

style.css
1:7  ⚠  Expected newline after "," (selector-list-comma-newline-after) [stylelint]
1:15 ⚠  Expected single space before "{" (block-opening-brace-space-before) [stylelint]
1:17 ⚠  Expected newline after "{" of a multi-line block (block-opening-brace-newline-after) [stylelint]
1:17 ⚠  Unexpected whitespace at end of line (no-eol-whitespace) [stylelint]
2:5  ⚠  Expected indentation of 2 spaces (indentation) [stylelint]
2:17 ⚠  Expected "#FFFFFF" to be "#ffffff" (color-hex-case) [stylelint]
2:17 ⚠  Expected "#FFFFFF" to be "#FFF" (color-hex-length) [stylelint]
2:25 ⚠  Expected newline after ";" in a multi-line rule (declaration-block-semicolon-newline-after) [stylelint]
2:25 ⚠  Unexpected whitespace at end of line (no-eol-whitespace) [stylelint]
3:5  ⚠  Expected indentation of 2 spaces (indentation) [stylelint]
3:15 ⚠  Expected single space after ":" with a single-line value (declaration-colon-space-after) [stylelint]
4:4  ⚠  Unexpected whitespace at end of line (no-eol-whitespace) [stylelint]
5:4  ⚠  Unexpected whitespace at end of line (no-eol-whitespace) [stylelint]
6:5  ⚠  Expected indentation of 2 spaces (indentation) [stylelint]
7:1  ⚠  Unexpected missing newline at end of file (no-missing-eof-newline) [stylelint]

Using this plugin can really help you enforce good practices when writing CSS. Continue by exploring the list of rules and overriding the ones from the standard config that you don’t agree with. You can later distribute these rules as your own set of your projects and teams. If there’s no rule that matches your need, you can always implement one yourself.

Do I Use

A lot of pain when writing CSS comes from trying to keep in mind which browsers are you targeting and what features can be used in them. Do I Use is a plugin that can help you make sure that the CSS you write is supported by your target browsers. You start by defining which browsers you are aiming to support. After that when you run the plugin, it will scan your code, validate it against the database of caniuse.com and trigger an error if some of the code is not supported.

Using this plugin is fairly simple. Install it:

npm i doiuse --save-dev

And update your gulpfile:

return gulp.src('style.css')
  .pipe(postcss([
    doiuse({
      browsers: ['ie >= 9', 'last 2 versions'],
    }),
    reporter()
]));

This configuration says that we’re aiming to support the last 2 major versions of each browser, as well as IE9 and newer.

To demonstrate the result, we’ll run the plugin on some fancy new CSS that uses features from the grid layout module.

body {
  display: grid;
  grid-columns: 200px 1% 1fr;
  grid-rows: auto 15px auto 15px auto;
}

Here’s what doiuse has to say about that:

style.css
11:2    ⚠  CSS3 Multiple column layout not supported by: IE (9), Firefox (43,44), Chrome (48,49), Safari (8,9), Opera (34,35), iOS Safari (8.1-8.4,9.0-9.2) (multicolumn) [doiuse]

It’s a shame, but at the moment of writing, the CSS grid module is still not mature enough. But let’s focus on the good part: we now have a tool to help us keep up with tracking browser capabilities!

Immutable CSS

One of the major sources of bugs and complexities in stylesheets is overriding CSS rules. Even using the modern debug tools it sometimes may be challenging to figure out where the style was overridden and why. This is why it is considered good practice not to override styles but rather add additional modifiers to selectors. The immutable CSS plugin can warn you when such cases of style overriding occur in your code.

It has two modes of operation. By default, it will only warn you if you try to override styles in different files. When multiple files are bundled into a single file, it will utilize source maps to figure out where the styles came from. This means it can play nicely with Sass imports or the postcss-import plugin. If you want to take it a step further, you can enable strict mode, which will also warn you if you override styles in a single file.

Here’s a quick example to demonstrate all of that. As usual, we’ll need to install the plugin first:

npm i immutable-css --save-dev

And enable the plugin with strict mode in the gulpfile:

return gulp.src('style.css')
  .pipe(postcss([
    immutableCss({
      strict: true
    }),
    reporter()
  ]))

Then feed it some nasty CSS:

.title {
  color: blue;
  font-weight: bold;
}

.title {
  color: green;
}

.article .title {
  font-size: 1.2em;
}

The plugin will conveniently report that the .title class has been mutated:

⚠  .title was mutated 3 times
[line 1, col 1]: /Users/pavels/Documents/projects/sandbox/postcss/style.css
[line 6, col 1]: /Users/pavels/Documents/projects/sandbox/postcss/style.css
[line 10, col 1]: /Users/pavels/Documents/projects/sandbox/postcss/style.css

You can tinker around with the plugin in its interactive playground.

CSS Stats and List Selectors

The last two plugins we’ll have a look at will be CSS stats and list selectors. They are a bit different from the plugins we’ve examined so far: they don’t aim at pointing out problems but rather provide data for custom analysis.

CSS stats provides extensive information about the stylesheet in general: how many rules, selectors or declarations are used, what are they, what is the average specificity of a selector or what font sizes occur in the code. This is just a sample of the information that can be found in the generated report. A more detailed description can be found on its GitHub page. You can also visit cssstats.com for examples of reports that can be generated by using the data from the plugin.

List selectors is a simpler plugin that focuses on extracting the list of selectors used in the stylesheets and grouping them by categories — class selectors, attributes, IDs or tags.

Both of these plugins can be used for all sorts of code analysis. Just a few examples are:

  • Keeping your specificity, size and amount of used entities at a certain threshold.
  • Making sure all of your selectors are written according to your coding style.
  • Ensuring your media queries are consistent.

These are just some ideas that came to mind. A more practical approach would be to set up all of the previous plugins, return to these two and have a look to see if they can convey any more useful information.

Wrapping It All Up

Code linting and analysis is just one of the ways one can use PostCSS, but it alone can add a lot of value to your development process, as well as save some time and a few grey hairs among your developers. Even though it’s becoming a common practice in other realms of programming it’s still frequently neglected when it comes to CSS. I believe that configuring PostCSS and these few plugins is a simple step to make your development much easier and more reliable.

Pavels JelisejevsPavels Jelisejevs
View Author

Pavels is a software developer from Riga, Latvia, with a keen interest for everything web-related. His interests range from back-end to front-end development, as well as analysis and automation. If you have something to discuss, you can always reach him via Facebook or LinkedIn.

AdvancedCSSpatrickcPostCSSpostprocessor
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week