645Days

645 days since this post was last revised. Specific details are likely out of date.

Stylelint is a new linting tool for CSS that runs via PostCSS. It’s quite new, having just released a 0.1.1 version. I love it. And I’d like to tell you why.

I like to think I practice good dental hygiene. I’ve invested in a sonic toothbrush and perform a good, thorough, brush twice daily.
However, as diligent as I am with my brushing, it’s only when I floss that I really catch all the problems.

It’s the same with my style sheets. However diligent I am as I write, invariably, decent tools can always highlight some shortcomings in the authoring.

Some things you should know:

I go friggin’ ape if someone doesn’t put a space here:

.please-no {
    display: block;
}           ⬆

My blood boils when people neglect to use a closing semi-colon:

.help-me {
    display: flex⬅
}

And don’t even get me started on inconsistent indentation!

However, I can accept these are largely personal hang ups. They don’t impact the efficacy of the style sheets. But what about folks using vendor prefixes when they don’t need to:

.jesus-wept {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}

Or duplicate properties in the same rule:

.we-all-do-it {
    display: block;
    transition: opacity .2s;
    color: #f90;
    background-color: 333;
    transition: background-color .3s; β¬…
}

This stuff isn’t merely pedantic style sheet man gone mad. It’s adding bloat, and sometimes causes tricky to find bugs. This is the place where ‘linters’ earn their keep.

In case the term ‘linting’ is Alien to you, you should know it’s a static analysis tool. It looks at the generated code and makes suggestions based on any number of pre-defined rules. For example, it might issue a warning if you use floats, or don’t put white-space where required. Generally speaking, you can use linters to enforce any kind of coding conventions you like and while more than useful when working alone, they can be a Godsend when working in teams: where many (careless) hands touch the code.

In the past I’ve used both css-lint and scss-lint. They are both excellent and easy enough to configure but it has always irked me that by default, I have to disable things I don’t want as opposed to enabling things I did. Furthermore, I’m not authoring in either CSS or SCSS (Sass) in my current projects so neither of these tools was particularly suitable.

Enter Stylelint

Stylelint fits my current needs perfectly. It runs via Node and PostCSS so can be piped easily into a Gulp based workflow. Furthermore, there are no linting rules enabled by default, you pick the ones you want to enforce and add them to your configuration. Here’s what a configuration looks like in my test setup (note, recently updated to show a config that works with v4 of stylelint):

// Config for linting
var stylelintConfig = {
    "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,
        "rule-no-duplicate-properties": true,
        "declaration-block-no-single-line": true,
        "rule-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,
        "rule-no-shorthand-property-overrides": true,
        "indentation": 4,
        "only-overrides-nested": true,
        "must-match-ecss-naming": true,
        "key-selectors-must-not-be-compound": true,
        "key-selectors-must-be-alone": true,
        "component-name-must-match-file-name": true        
    }
};

Your config can look totally different. If there are only two things you care about, you only need two in there.

There’s plenty more rules available, take a looky-see at the available rules

Extensible

If all that wasn’t enough, you should know that Stylelint is also extensible. You can easily extend its capabilities by adding your own plugin rules (just a JS file for each rule). You can see in the example configuration above that the last 5 rules are not ‘stock’ rules. Instead they are custom plugins to police particulars of an existing project and codebase.

One day, I hope to create something that catches this kind of thing:

.o-m-g {
    width: 30px;
    max-width: 30px;
    min-width: 30px;
}
BTW – when ‘full-stack’ developers spout that CSS isn’t really a programming language I’d like hit them with a big fish tattooed with CSS like this – the sort of bullshit CSS they commonly write.

Setup gotchas

I’m always one of the first people to ask a bunch of stupid questions on a new project’s GitHub issues. Stylelint was no different. I was getting false positives on rules, incorrect line numbers. I knew it was something I was doing. Transpires it was partly setup, and partly my understanding of how and where to run the linter. A brief tangent:

I’m writing a peculiar version of CSS at present. It’s incredibly close to Sass but it isn’t Sass. I compile files with a *.css extension into another CSS file with PostCSS. This let’s me have niceties like variables and mixins, globbing imports and more, but without giving the ‘great unwashed’ (my loving term for other members of the team I can’t trust to not use loops and functions given half a chance (that’s cardinal sin number 7)) the CSS equivalent of nuclear warheads.

So, given this, I wanted Stylelint to run on my authoring CSS, not my resultant CSS. Thanks to @davidtheclark I needed to switch up my Gulp tasks and move Stylelint from within my usual styles task and into it’s own lint-styles task, which also gets run from the styles task. Example:

gulp.task("lint-styles", function() {
    return gulp.src("preCSS/**/*.css")
    .pipe(postcss([
        stylelint(stylelintConfig),
        reporter({
            clearMessages: true,
        }),
    ]));
});


gulp.task("styles", ["lint-styles"], function() {
    var processors = [
        postcssImport({glob: true}),
        mixins,
        simplevars,
        nestedcss,
        postcssColorFunction(),
        mqpacker,
        autoprefixer({browsers: ["last 2 version", "ios 6", "android 4"]}),
        csswring
    ];
    return gulp.src("preCSS/styles.css")
    .pipe(postcss(processors).on("error", gutil.log))
    .pipe(gulp.dest("css/"))
    .pipe(reload({stream: true}));
});

Now, if I write something idiotic (daily), I’ll get a nice little warning:

Screen Shot 2015-07-13 at 22.21.19
If you use Sublime Text 3, my esteemed colleague, Pete Griffiths, has written an excellent Sublime plugin to provide Stylelint linting feedback directly in Sublime. Woot!! Go grab it here.

Summary

Stylelint is a great project. It catches school-boy CSS authoring errors diligently and integrates quickly and easily into an existing PostCSS/Gulp based workflow. I can’t wait to start policing CSS code-bases with it.

It’s like flossing your style sheets, but without needing to contort your face.

Ben Frain Developer, Author: 'Enduring CSS', 'Responsive Web Design with HTML5 & CSS3'.