Chapter 9. Tooling for an ECSS approach

In this final chapter we will look at some of the tooling that's available free and open source to facilitate writing sane and maintainable style sheets.

When authoring the CSS for an enduring project, the technology employed to produce the CSS should be largely immaterial. We should always be aware that a better or more efficient tool may become available to achieve our aims and when possible, and if preferable, it should be embraced.

Therefore, it shouldn't matter whether Sass, PostCSS, LESS, Stylus, Myth or any other CSS processor is employed to author the style sheets. The authored style sheets should be as easy to migrate to another meta-language as possible, if and when needed.

Furthermore, the CSS processor employed should best serve the needs of the project as a whole and not merely the preferences of any individual author. That said, there are some necessary capabilities for the CSS processor so we will cover that briefly next.

CSS requisites for CSS processors

I consider a CSS processor for style sheet authoring essential. This allows a differentiation between 'authoring' style sheets (the style sheets that the author writes in their CSS processor of choice) and the 'resultant' CSS (the compiled and minified CSS that gets served to the user).

Despite stating that a CSS processor is essential, the requisite features needed are fairly trivial:

Building CSS from authoring style sheets

A build system of some sort is required to compile the authoring style sheets (with their variables, mixins and the like) into plain CSS. There are many tools available to perform this task e.g Grunt, Gulp and Brocolli to name just a few. Just as there is no universally 'right' CSS processor, so there is no universally 'right' build tool.

Besides merely compiling authoring style sheets into CSS, good tooling can provide further benefits.

Save to compile, the journey of an ECSS style sheet

In terms of tooling, at the time of writing, I currently write ECSS with the help of Gulp and PostCSS plus its many and varied plugins.

The style sheet authors write into a partial CSS file (with a *.css file extension), using a syntax that is very similar to Sass.

On saving an authoring style sheet, the Gulp watch task notices the file change and first starts the authoring style sheets linting task. Then, providing all is well, it compiles the partial authoring style sheets to CSS, auto-prefixes them and then BrowserSync injects the changed CSS directly into the webpage I'm working on. Typically, a source map file is also created as some authors find them easier for debugging. All this happens before I can alt+tab into my browser window or even move my gaze from text editor to browser window.

Here's an example gulpfile.js that demonstrate how PostCSS might be setup in a Gulp based build tool.

//PostCSS related
var postcss = require("gulp-postcss");
var postcssImport = require("postcss-import");
var autoprefixer = require("autoprefixer");
var simpleVars = require("postcss-simple-vars");
var mixins = require("postcss-mixins");
var cssnano = require("cssnano");
var reporter = require("postcss-reporter");
var stylelint = require("stylelint");
var stylelinterConfig = require("./stylelintConfig.js");
var colorFunction = require("postcss-color-function");
var nested = require("postcss-nested");
var sourcemaps = require("gulp-sourcemaps");

// Create the styles
gulp.task("styles", ["lint-styles"], function () {

    var processors = [
        postcssImport({glob: true}),
        mixins,
        simpleVars,
        colorFunction(),
        nested,
        autoprefixer({ browsers: ["last 2 version", "safari 5", "opera 12.1", "ios 6", "android 2.3"] }),
        cssnano
    ];

    return gulp.src("preCSS/styles.css")

    // start Sourcemaps
    .pipe(sourcemaps.init())

    // We always want PostCSS to run
    .pipe(postcss(processors).on("error", gutil.log))

    // Write a source map into the CSS at this point
    .pipe(sourcemaps.write())

    // Set the destination for the CSS file
    .pipe(gulp.dest("./build"))

    // If in DEV environment, notify user that styles have been compiled
    .pipe(notify("Yo Mofo, check dem styles!!!"))

    // If in DEV environment, reload the browser
    .pipe(reload({stream: true}));
});

With Gulp the build choices are fairly limitless, this is merely an illustration. However, note how the the first thing the 'styles' task does, it run the 'lint-styles' task.

As mentioned in previous chapters, the linting of style sheets is a very important step on a project where multiple style sheets authors are involved. Let's look a little more at that next.

Stylelint

Stylelint is a Node based linting tool for the static analysis of style sheets. In layman's terms it will analyse your style sheets for the rules you specifically care about and warn you of any problems.

The linting job fails the build if any authoring errors are found. Typically it's most beneficial to have linting running in two places. In the text editor (e.g. Sublime) and in the build tool (e.g. Gulp). This way, if an author has the requisite text editor then the editor based linting indicates problems before an author even clicks 'save'.

Even if a user doesn't have in-editor linting available, the linting job runs via Gulp on save. The build step prevents compiled code making its way to production (as continuous integration software would also fail the build).

This is a massive time saver and has proved invaluable when it comes to peer-reviewing code and performing quality assurance tests.

Here is an example .stylelintrc configuration for Stylelint (this is for v5 of Stylelint so future/previous versions may vary slightly):

// Config for linting
module.exports = { 
    "rules": {
        "block-no-empty": true,
        "color-no-invalid-hex": true,
        "declaration-colon-space-after": "always",
        "declaration-colon-space-before": "never",
        "function-comma-space-after": "always",
        "function-url-quotes": "double",
        "media-feature-colon-space-after": "always",
        "media-feature-colon-space-before": "never",
        "media-feature-name-no-vendor-prefix": true,
        "max-empty-lines": 2,
        "number-leading-zero": "never",
        "number-no-trailing-zeros": true,
        "property-no-vendor-prefix": true,
        "declaration-block-no-duplicate-properties": true,
        "block-no-single-line": true,
        "declaration-block-trailing-semicolon": "always",
        "selector-list-comma-newline-after": "always-multi-line",
        "selector-no-id": true,
        "string-quotes": "double",
        "value-no-vendor-prefix": true,
        "function-linear-gradient-no-nonstandard-direction": true,
        "selector-no-universal": true,
        "declaration-block-no-shorthand-property-overrides": true,
        "indentation": 4,
        "selector-max-specificity": "0,2,0"
    } 
}

This is just an example, you can set whichever rules you care about from the ever expanding list. If using these sort of tools for the first time, you might also find it useful to download/clone ecss-postcss-shell. It's a basic Gulp setup to run the authored style sheets through PostCSS and lints the styles with Stylelint.

If that wasn't enough, Stylelint is extensible. It's easy to add additional functionality. For current builds ECSS projects in my workplace we have additional Stylelint rules to:

These offer bespoke quality assurance that would be time consuming and error prone to perform 'by hand'.

In case I'm not making it clear I want you to know that I love Stylelint and think linting is an indispensable piece of tooling for large projects with multiple authors. I simply cannot recommend it highly enough.

Optimisation

When CSS is heading for production, it takes an extra step through cssnano. It's a fantastic and modular CSS minifier by the extraordinarily talented Ben Briggs. Highly recommended.

Besides the more obvious minification step that cssnano provides, there are a number of micro-optimisations you can perform on your CSS just by incorporating plugins from the PostCSS eco-system. For example, by consistently ordering your CSS declarations, Gzip can compress the style sheet more effectively. That's not a job I want to do manually but the postcss-sorting plugin can do it for free. Here's comparison of Gzip file sizes using the various declaration sorting configurations.

To exemplify, I took a large test CSS file, unsorted and it Gzipped to 37.59 kB. Here are the file sizes of that same file when Gzipped after using the other declaration sorting configurations:

So at best we gain a saving of just under 1% of the original size. A tiny economy but one you can effectively get for free.

There are other such economies such as grouping alike media queries but I'll leave these micro-optimisations for you to explore should they pique your interest.

Summary

In this chapter we've covered tooling to facilitate constant code quality and an improved style sheet authoring experience. However, you should be aware that out of everything we have covered, the specific tools listed here are likely to be the most short-lived. Tooling technology moves at a blistering pace. In just three years I went from vanilla CSS, to Sass (with scss-lint), to PostCSS and Stylelint while also moving from GUI build tools like CodeKit to JavaScript build tools Grunt, then Gulp and now NPM scripts.

I have no idea what the best choice will be in 6 months time so the take away is to think about how tooling can improve the style sheet authoring experience across teams, not necessarily what the current tools are.

Be monogamous in your personal relationships and a philandering whore in your choice of tools and techniques. The Way Of Pragmatic Coding