Floss your style sheets with Stylelint
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 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;
}
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:

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.
Why show errors instead of autoconverting them to specified code style rules?