Menu Search
Jump to the content X X
SmashingConf London Avatar

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf London, dedicated to all things web performance.

Stylelint: The Style Sheet Linter We’ve Always Wanted

Everyone wants a clean, consistent code base, no matter the language. Developers are accustomed to setting up linters in programming languages such as JavaScript and Python, but they rarely use a linter for style sheets. In this article, we’ll look at stylelint1, a linter for style sheets.

We will learn why linting a style sheet matters, how stylelint brings order to a style sheet and how we can avoid errors. Finally, we will learn how to use stylelint and start linting as soon as possible.

Further Reading on SmashingMag: Link

Why Linting Is Important Link

A linter is a tool that analyzes code and reports errors when a piece of code doesn’t pass the rules defined in the linter’s configuration.

Many of us work on code bases that many people have access to. If no strict rules on coding style are adhered to, the code could become a mess very fast. Maybe your code is already a mess, and you want to clean it up and maintain this cleanliness over time. Even if you work on style sheets alone, you’ll still want the code to be consistent.

Of course, your team might have code styles rules written in plain text in some wiki somewhere. But there is always the human factor to account for: People make mistakes — never on purpose.

And even if you are obsessed with following the rules of a proper coding style, your colleagues or the contributors to your open-source project might not be. Without a linter, you’d need to check the code for styling and errors by yourself. No person should spend time on things that can be automated. A linter will significantly decrease the time spent on code review because you will not be spending time checking styles and writing a pile of comments about every error. You will be free to examine what the code does, rather how it looks.

Stylelint Link

Stylelint356 is a mighty, modern style sheet linter written in JavaScript by David Clark7, Richard Hallows8, Evilebot Tnawi9 and community10. It is powerful in its speed, variety and quality of rules, and it’s totally unopinionated. Stylelint has over a hundred rules, and the number is growing. Fear not, though: All rules are disabled by default, and you enable only the ones you want. Stylelint can lint not only CSS but also Sass11, SugarSS12 and any other syntaxes that PostCSS13 can parse (because stylelint is based on it).

Stylelint is to style sheets what ESLint14 is to JavaScript.

Rules Link

Stylelint has over a hundred rules15, which can be divided into three groups: rules for styling, rules for the maintainability of code, and rules that check errors that would change what the code does in a browser. Style rules check for spacing (such as around colons), line breaks, indentation, etc. Rules for maintainability might report if an ID is used in a selector or if the !important keyword is used in a declaration. Rules for checking for errors might report an incorrect HEX color or a shorthand property that overrides another declaration.

I will not go over the style rules here (there are a ton of them). Rather, I want to describe some of the rules that help with maintainability and prevent errors.

The rule for preventing shorthand properties from overriding other declarations (or, in stylelint parlance, declaration-block-no-shorthand-property-overrides) would prevent a situation like this:

div {
    padding-left: 20px; /* This property is overridden. */
    padding: 10px;
}

Stylelint also prevents invalid HEX colors (color-no-invalid-hex):

p {
    color: #44;
}

And it prevents duplicate properties (declaration-block-no-duplicate-properties):

p {
    color: #000; /* This property is overridden. */
    margin: 0 0 1.25em;
    color: #777;
}

You may use the old syntax for gradients. Stylelint will check for it (function-linear-gradient-no-nonstandard-direction):

/* incorrect property */
.block {
    background: linear-gradient(bottom, #fff, #000);
}

/* correct property */
.block {
    background: linear-gradient(to bottom, #fff, #000);
}

Using the !important keyword on a property can cause problems down the line when you need to override the property with another rule. Just avoid !important altogether with the declaration-no-important rule.

Using an ID in a selector (#main) and using a type selector (i.e. a selector based on an HTML element — for example, .block p) might be forbidden in your development methodology (for example, BEM16). In this case, selector-no-id and selector-no-type come in handy.

Sometimes you’ll misspell something or forget to add something to a style sheet. In the case of animation, no-unknown-animations will report if an animation’s name has no corresponding @keyframes rule.

And why would you want to bother with prefixes in values, properties names and selectors when we have Autoprefixer17? Let Autoprefixer take care of that, and prevent prefixes from being added with the rules value-no-vendor-prefix, property-no-vendor-prefix and selector-no-vendor-prefix.

There are, of course, many more rules18 in stylelint.

Plugins Link

Beside the default rules, stylelint also supports plugins, so you can extend it with new rules. Not many plugins are available19 right now, but the ones we have are very handy.

Sometimes developers over-nest. While all preprocessors support nesting, nesting rules very deep results in increased selector specificity and leads to problems with maintaining those rules. Here is a typical example:

.header {
    .nav {
        .item {
            .link {
                color: blue;

                &:hover {
                    color: red;
                }
            }
        }
    }
}

This renders as follows:

.header .nav .item .link {
    color: blue;
}
.header .nav .item .link:hover {
    color: red;
}

Stylelint has no rule for this problem out of the box, but there is a plugin20 (stylelint-statement-max-nesting-depth) that adds a rule for nesting depth.

To use any plugin, install it first:

npm install stylelint-statement-max-nesting-depth --save-dev

Then, add the plugin to the configuration file in the plugins array. Add the new rule and configure it:

{
    "plugins": [
        "stylelint-statement-max-nesting-depth"
    ],
    "rules": {
        "statement-max-nesting-depth": 2
    }
}

In the configuration above, we’ve set the nesting depth to a maximum of two. So, we would be prompted to simplify our earlier example to a lower nesting depth (in this case, two levels):

.nav {
    .link {
        color: blue;

        &:hover {
            color: red;
        }
    }
}

Or we could simplify further to one level:

.nav-link {
    color: blue;

    &:hover {
        color: red;
    }
}

I will not go over all of the plugins here, but rather will recommend a few:

  • Prevent qualified selectors21, such as ul.nav, div#main and input[type="submit"]. (Each option can be enabled separately.)
  • Enforce shorthand values22 whenever possible.
  • If you are following the BEM or SUIT methodology, then you might want to check the validity of your selectors against it. Plugin23 stylelint-selector-bem-pattern has predefined patterns for BEM and SUIT and can be configured for other methodologies.

If you want a new rule, you can write your own plugin24.

Configuration Files Link

Configuring is the most difficult part about using a linter — and the most time-consuming. But there are shortcuts and different strategies that make stylelint easier to configure.

Your configuration can grow to be very big, so the most convenient way to store stylelint’s configuration is in a separate JSON file, named .stylelintrc. This way, the file can be used in the command line interface, in a build tool and in a code editor.

A very simple configuration might look like this:

{
    "rules": {
        "color-hex-case": "lower",
        "color-hex-length": "short",
        "color-no-invalid-hex": true
    }
}

There are three strategies for configuration. The first, a simple one, is to extend someone else’s configuration (which stylelint supports) and then add, disable or tweak the rules you want to change. Developers have made configuration that probably fit most needs. You can install one as an npm package:

npm install stylelint-config-standard --save-dev

Then, in your own configuration file, you would extend theirs and override any rules as needed:

{
    "extends": "stylelint-config-standard",
    "rules": {
        "indentation": "tab",
        "number-leading-zero": null
    }
}

In this example, we’ve extended stylelint-config-standard and changed the indentation rule to be “tabs” and disabled the number-leading-zero rule.

You can extend not only configurations shared via npm, but local ones, too. The documentation has more on extending and sharing configurations25.

The second strategy is to start with an empty file and progress slowly by adding rules as you need them. For example, you might not care about coding style just yet and just want to focus on preventing errors:

{
    "rules": {
        "color-no-invalid-hex": true,
        "declaration-block-no-duplicate-properties": true,
        "declaration-block-no-shorthand-property-overrides": true,
        "function-linear-gradient-no-nonstandard-direction": true
    }
}

Later, you can add more rules.

The third strategy is to go over all of the rules and configure every one. I prefer this strategy because I want to check as much as possible and use stylelint at its full power. Sure, it’s the most time-consuming strategy, but it yields the best result. To make it easier, stylelint’s developers have created an example configuration file26 with all rules.

Every enabled rule has an error severity. This means that any rule that is not met will fail the test. The severity level for any rule can be lowered to a warning, which will prevent a test from failing. This is useful if you have just introduced a rule and don’t want a build to fail as the team is adjusting to the new rule.

{
    "rules": {
        "color-hex-case": ["lower", { "severity": "warning" }]
    }
}

In this example, stylelint will warn if a HEX color is miswritten, but it won’t throw an error.

Sometimes we need to put something in a style sheet that our stylelint configuration forbids. For example, we are forbidden to use the !important keyword, but we might need to use it in one place to override some third-party widget. We wouldn’t want to disable the rule because of this exceptional case. But we wouldn’t want to see the error every time either. Luckily, we can disable a particular rule in a line of CSS by adding a comment:

.widget {
  display: none !important; /* stylelint-disable-line declaration-no-important */
}

Or we can disable stylelint for a chunk of CSS:

/* stylelint-disable */
.third-party-code {}
/* stylelint-enable */

Usage Link

Stylelint can be used in many ways27: in the command line, in a build tool (such as Gulp, Grunt or Webpack), in a code editor28 or as a Git pre-commit hook29 for staged changes in Git repository. I will focus here on two ways.

Command Line Link

Using the command line is useful when you want to lint a project that doesn’t have stylelint or you want to use stylelint in an npm script.

Install stylelint globally:

npm install stylelint -g

Then, it will be available everywhere in your terminal:

stylelint "styles/**/*.css"

This command will lint all CSS files in the styles directory and any of its subdirectories.

To lint SCSS or SugarSS files, add the syntax option:

stylelint "styles/*.scss" --syntax scss

The configuration file can be specified explicitly:

stylelint "styles/*.css" --config bar/myStylelintConfig.json

If it’s not specified explicitly, stylelint will look for a .stylelintrc file in the current working directory.

Gulp Link

To use stylelint with Gulp, use it as PostCSS plugin. You’ll need to install the following packages:

npm install gulp-postcss stylelint postcss-reporter --save-dev

gulp-postcss30 is a runner for all PostCSS plugins, and postcss-reporter31 outputs much nicer errors and warnings from stylelint.

var postcss = require('gulp-postcss');
var reporter = require('postcss-reporter');
var stylelint = require('stylelint');

gulp.task('lint:css', function() {
    return gulp.src('src/**/*.css')
        .pipe(postcss([
            stylelint({ /* options */ }),
            reporter({ clearMessages: true })
        ]));
});

The output would look like:

The terminal window with results from a stylelint Gulp task32

The terminal window with results from a stylelint Gulp task (View large version33)

To lint a style sheet other than CSS, you’ll need to install the appropriate syntax. For example, to lint SCSS, we’d need to install postcss-scss34:

npm install postcss-scss --savedev

Then, configure gulp-postcss to use this syntax:

var postcss = require('gulp-postcss');
var reporter = require('postcss-reporter');
var stylelint = require('stylelint');
var scss = require("postcss-scss");

gulp.task('lint:css', function() {
    return gulp.src('src/**/*.scss')
        .pipe(postcss(
            [
                stylelint({ /* options */ }),
                reporter({ clearMessages: true })
            ],
            {
                syntax: scss
            }
        ));
});

You can specify the configuration file explicitly; otherwise, stylelint will look for .stylelintrc.

Conclusion Link

Stylelint356 is a powerful style sheet linter. It brings clarity to code and saves you from errors. It’s useful for everyone: individual developers, teams and open-source maintainers. Once you start using it, you will hear no more comments like, “You forgot to add a space here” or “You forgot to remove it there.” Happy developing, and may you have a peaceful code review.

(rb, ml, al, il)

Footnotes Link

  1. 1 http://stylelint.io/
  2. 2 https://www.smashingmagazine.com/2012/10/why-coding-style-matters/
  3. 3 https://www.smashingmagazine.com/2008/08/7-principles-of-clean-and-optimized-css-code/
  4. 4 https://www.smashingmagazine.com/2015/09/eslint-the-next-generation-javascript-linter/
  5. 5 https://www.smashingmagazine.com/2015/12/introduction-to-postcss/
  6. 6 http://stylelint.io/
  7. 7 http://davidtheclark.com/
  8. 8 http://richardhallows.com/
  9. 9 https://github.com/evilebottnawi
  10. 10 https://github.com/stylelint/stylelint/graphs/contributors
  11. 11 http://sass-lang.com/
  12. 12 https://github.com/postcss/sugarss
  13. 13 http://postcss.org
  14. 14 https://www.smashingmagazine.com/2015/09/eslint-the-next-generation-javascript-linter/
  15. 15 http://stylelint.io/user-guide/rules/
  16. 16 https://en.bem.info/method/naming-convention/#css-selector-naming-convention
  17. 17 https://github.com/postcss/autoprefixer
  18. 18 http://stylelint.io/user-guide/rules/
  19. 19 https://www.npmjs.com/search?q=stylelint-plugin
  20. 20 https://github.com/davidtheclark/stylelint-statement-max-nesting-depth
  21. 21 https://github.com/timothyneiljohnson/stylelint-selector-qualifying-element
  22. 22 https://github.com/timothyneiljohnson/stylelint-value-shorthand
  23. 23 https://github.com/davidtheclark/stylelint-selector-bem-pattern
  24. 24 http://stylelint.io/developer-guide/plugins/
  25. 25 http://stylelint.io/user-guide/configuration/#extends
  26. 26 http://stylelint.io/user-guide/example-config/
  27. 27 http://stylelint.io/user-guide/
  28. 28 http://stylelint.io/user-guide/complementary-tools/
  29. 29 https://github.com/okonet/lint-staged
  30. 30 https://github.com/postcss/gulp-postcss
  31. 31 https://github.com/postcss/postcss-reporter
  32. 32 https://www.smashingmagazine.com/wp-content/uploads/2016/05/terminal-gulp-reporter-opt.png
  33. 33 https://www.smashingmagazine.com/wp-content/uploads/2016/05/terminal-gulp-reporter-opt.png
  34. 34 https://github.com/postcss/postcss-scss
  35. 35 http://stylelint.io/

↑ Back to top Tweet itShare on Facebook

Aleks Hudochenkov is a front-end developer, who cares about the quality of user interface, page performance, and aesthetics. Always wants to bring better tools and workflows for developers, that why also cares about code maintainability and readability. The author of PostCSS Sorting and PostCSS follower.

  1. 1

    Serg Hospodarets

    May 25, 2016 11:16 am

    Thanks for the nice article.
    Are there any plugins for IDEs or plans regarding that?

    1
  2. 3

    Ivan Čurić

    May 25, 2016 12:12 pm

    I really wanted to try Stylelint, but the plugins for Sublime Text and Atom are nowhere near mature enough.
    The ST one randomly stops linting after a number of lines, or won’t lint at all under certain circumstances. The Atom one starts blocking the main thread so much that you can’t even type properly when working on larger files. Sticking with sass-lint for now :(

    0
    • 4

      Aleks Hudochenkov

      May 25, 2016 9:51 pm

      Can you create in issues in plugins repositories with your problems, please? I believe it’ll help fix these problems.

      0
    • 5

      David Clark

      May 27, 2016 4:08 pm

      Lots of people have been using the editor plugins successfully for some time. However, you’re not the only one who’s experienced problems. If you like stylelint’s approach and capabilities, but have trouble with the editor plugins, you can use the CLI — or, since all the linter plugins are open source, the best solution to the problem is to pitch in and solve it.

      1
  3. 6

    Katherine Delorme

    May 25, 2016 9:28 pm

    Hello there,

    Out of curiosity of wanting to try Stylelint is there a text editor it works well in? I’m going off of Ivan Čurić comment. I primarily use Atom now and also have Sublime Text. Since it sounds like it stops working at some point on both, what text editor can I use with it. I do plan to try it out on Atom and possibly Sublime Text.

    Also this is a nice article. I wasn’t aware of linting for css, or at least consciously maybe I’ve heard it mentioned, but am glad to have learned something new. I tweeted this article out on Twitter in case there were others like myself, spread the knowledge.

    0
    • 7

      Aleks Hudochenkov

      May 25, 2016 9:48 pm

      Thank you, Katherine!

      I use stylelint in Sublime Text. Works good for me.

      0
      • 8

        Katherine Delorme

        May 26, 2016 2:48 pm

        Okay, thank you for replying. I’ll give it a try on Sublime Text as well and see what I think.

        0
      • 9

        Katherine Delorme

        June 15, 2016 3:26 am

        Hello there,
        Just wanted to be friendly and come back with an update. Over the weekend I got a css style sheet linter for Atom. I made an attempt for Sublime but the run around was too much and I’m already content with Atom. So far it works well and I like it. I had a minor report for one project and few changes to make. On an older project it was a long list and I quite enjoyed going through and making changes. The only hiccup was the error ‘Don’t use IDs in selectors’ after some research it made sense. I read two arguments, against and for it. In the end it caused me to rethink the amount of ids I use so learning moment and reflection.

        2
  4. 10

    Victor Longon

    May 26, 2016 7:16 am

    Great article. I use with atom. Works like a charm. Way better to get live feedback when writing on thr editor than running from a build system, IMHO.

    Thanks for sharing!

    1
  5. 11

    Hey, I’m currently using scss-lint which works very well with PhpStorm. Would there be any significant benefit to switch to stylelint? Looks like it’s doing the same job.

    1
    • 12

      David Clark

      May 27, 2016 4:12 pm

      If you’re writing SCSS, and scss-lint does what you need, then great! They are both linters so serve essentially the same purpose — but they are not the same tool. They have overlapping but not identical feature sets. You might get “significant benefit” from switching to stylelint if you want to use some of the features stylelint has that scss-lint does not.

      0
  6. 13

    Andreas Olsson

    May 27, 2016 6:42 am

    I’ve been using atom-linter with extension for JavaScript if Sass. Now and sometimes gulp-scss-kint and gulp-jslint.
    Does this tool do any additional stuff that would make me want to use it over the other ones?

    0
  7. 14

    David Clark

    May 27, 2016 4:14 pm

    Thanks for writing this article, Aleks! I just wanted to note that I’m not the only author of stylelint. There have been a few other significant contributors that I wouldn’t want to shortchange! If it’s possible to alter the sentence and clarify that point, I’d appreciate it. Thanks!

    0
  8. 16

    Vikram Gupta

    May 28, 2016 7:51 pm

    Heyy,

    Nice article. One question, can it be used to lint less because I tried and it was giving css functions(mixins) as unkbown word because of the character ‘.’.

    0
    • 17

      Aleks Hudochenkov

      May 31, 2016 9:34 am

      Yes, it supports Less, but this support is experimental while Less parser for PostCSS is unstable.

      0
  9. 18

    stylelint-statement-max-nesting-depth is deprecated in favor of stylelints native max-nesting-depth https://github.com/davidtheclark/stylelint-statement-max-nesting-depth

    1
    • 19

      Aleks Hudochenkov

      June 1, 2016 8:42 pm

      Thank you for pointing that out. Stylelint is developing and improving very quickly :)

      0
  10. 20

    Richard Hallows

    June 4, 2016 10:49 pm

    Aleks, thanks for writing this article. It’s excellent!

    I’ve a couple of notes for you:

    1. In addition to the native max-nesting-depth rule that Sjur mentioned above, both the “prevent qualified selectors” and “enforce shorthand values” plugins were rolled into the core rules as selector-no-qualifying-type and shorthand-property-no-redundant-values respectively. You were right to highlight the stylelint-selector-bem-pattern plugin though. It’s a great example of a plugin as it focuses on a CSS methodology rather than standard CSS syntax. Another good plugin (pack) to highlight is stylelint-scss – a collection of SCSS-syntax specific rules. The stylelint-scss developers have done an excellent job adopting the stylelint naming and developmentconventions for a consistent user experience.

    2. We now recommend using gulp-stylelint, rather than the gulp-postcss and postcss-reporter combination outlined in this article. gulp-stylelint uses stylelint’s built-in formatters which are more fully-featured than the generic postcss-reporter. Also, keep an eye on grunt-stylelint, which promises to bring the same quality reporting to the grunt ecosystem.

    1
  11. 21

    Pubudu Kodikara

    June 22, 2016 11:25 am

    Hi, this is not related to the topic but can you please tell us what is the theme you use in your terminal (Shown in the screenshot)?

    0

↑ Back to top