How to lint your Sass/CSS properly with Stylelint

Last updated on the .

Stylesheet linting. Not many people do it. Many more people should—especially diverse teams that have a lot of hands touching the codebase.

In this article I’m going to talk about why we should lint our stylesheets, and how to implement stylesheet linting into our build pipelines for both vanilla CSS and Sass.

Table of contents:

  1. Introduction
    1. What is linting?
    2. Why should we lint our stylesheets?
    3. Introducing Stylelint
  2. Setup
    1. How to lint your CSS
    2. How to lint your Sass
  3. Extending Stylelint with plugins
    1. Case study: Linting in practice
  4. Afterword
    1. A one click install gulp build pipeline for Sass


What is linting?

Linting is the process of checking the source code for Programmatic as well as Stylistic errors. This is most helpful in identifying some common and uncommon mistakes that are made during coding. They’re essentially a spell checker for programming languages. While linting is useful when working alone, they really pay dividends when working in teams: where many (careless) hands touch the code.

A Lint—or a Linter—is a program or tool that supports linting (verifying code quality). They are available for most languages like C, Python, JavaScript, CSS, etc.

Why should we lint our stylesheets?

There are many reasons to lint stylesheets. It maintains code consistency, it highlights errors in the codebase, it helps reduce unnecessary code, and it also helps prevent lazy coding.

Let’s take a look at a couple of examples.

.no-space-after-colon {

.no-semicolon {
    position: relative 

Linters are very good at spotting stylistic issues like these. Setting up stylistic linting rules aren’t essential, but they help keep the code consistent. Also, I don’t know about you, but these 2 stylistic errors above are a pet peeve of mine.

.invalid-hex {
    color: #FFF00G;

They’re also very good at spotting errors like invalid hex colours, which may have resulted from a type error. Errors like this are capable of breaking important visual styling if not caught.

.unnecessary-prefixes {
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;

There are a fair few CSS3 rules that don’t need to be prefixed anymore in order to work. Linting spots these rules and helps to remove unnecessary and deprecated code. Linting prefixes is especially useful when paired with Autoprefixer—this lets you remove all prefixes, and only insert the ones you require for your target audience.

.duplicate-rule {
    display: block;
    transition: opacity .2s;
    color: #444;
    background-color: #eee;
    transition: background-color .4s; 

Duplicate rules are a common form of erroneous code. What if the developer meant for both opacity and background-colour to be transitioned? In the case above, the opacity transition is lost. Linting would highlight this error.

Convinced yet? If you’re not, keep reading…

Introducing Stylelint

Stylelint is a super extendable, and unopinionated CSS linter written in JavaScript. It’s the latest and greatest in CSS linting. It supports the latest CSS syntax, understands CSS-like syntaxes, and is extendable with plugins. What’s more, because it’s powered by JavaScript instead of Ruby, it’s much, much faster than scss-lint.

Stylelint is a mighty, modern CSS linter that helps you enforce consistent conventions and avoid errors in your stylesheets.

The linter is powered by PostCSS, so it understands any syntax that PostCSS can parse, including SCSS.

PostCSS is a tool for transforming styles with JS plugins. These plugins can lint your CSS, support variables and mixins, transpile future CSS syntax, inline images, and more.

The mantra of PostCSS is do one thing, and one thing well; so it’s all about plugins. There’s currently more than 200 plugins available for PostCSS, and because they’re all written in JavaScript, they run rapid fast!

PostCSS and Stylelint are what we’ll be using to lint our stylesheets in the next section.


Stylelint config files

The beauty of Stylelint is how unopinionated it is. You build your own ruleset from the ground up, so it can be as opinionated or unopinionated as you choose—you don’t have to spend time disabling rules you don’t want in order to get started.

The Stylelint rule documentation is a good primer for getting started. They also provide a stylelint standard config file for you to use in your projects that is rather well thought out.

For us getting started though, we’re going to use a nice and compact config file that covers the bare essentials. Personally, I think it’s a better starting config than the one Stylelint provide, as it gives you space to build on top of it, rather than having to disable the rules you don’t want.

It looks something like this:

"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": 5,
  "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-space-before": "never",
  "selector-list-comma-newline-after": "always",
  "selector-no-id": true,
  "string-quotes": "double",
  "value-no-vendor-prefix": true

I recommend reading through the Stylelint rule documentation and building on this to create your ideal linting configuration. For now, let’s get into setting up our pipeline using these rules.

How to lint your CSS

Let’s start with linting vanilla CSS stylesheets. You’ll be amazed at how easy it is to setup! The tools you’ll need to install gulp-postcss, postcss-reporter, and stylelint. Let’s do that now.

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

And this is the gulpfile to wire it all together:

How easy was that?! I make that 50 lines of code—including linting rules and imports. Make sure to update the source locations to match the ones in your project!

What’s even more amazing, is only a single line of code needs to be modified in order to enable Sass support! Let’s cover that now…

How to lint your Sass

Linting Sass files is super easy with PostCSS. The only difference between linting CSS and Scss, is you need to make PostCSS understand the .Scss syntax, and that’s as easy as installing postcss-scss, and changing one line in the task above.

npm install postcss-scss --save-dev

  return gulp.src(
    .pipe(postcss(processors, {syntax: syntax_scss})); 

Here’s the gulpfile in full for linting Sass files:

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

So, so easy! And that’s it! You can now lint both CSS and Sass files!

Keep on reading if you want to know about extending Stylelint with plugins, and why you’d want to do that, by going through a case study.

Extending Stylelint with plugins

Just like PostCSS, Stylelint is extendable via plugins, which is awesome!

Let’s run through a quick scenario where linting would help improve code readability, and help kick lazy devs up the butt when they try and hack an easy win into the codebase.

Case Study: Linting in practice

The project manager who likes to code

How about this for a scenario. A project manager is managing a new web app that’s in development, and decides—in order to free up some valuable dev time on the project—to have a go at adding a feature. The feature is to add a box shadow to the hover state of a component, and to also add a hover state to the links of a child component.

What’s the worst that could happen?

Here is the code that the project manager adds to the project:

.component {
  position: relative;

  &:hover { 
    box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75);

    .component__child { 
      ul { 
        li { 
          a { 
            &:hover { 
              text-decoration: underline;


Selector nesting in Sass is bad—use a linter!

Selector nesting is a necessary evil when developing using Sass; it’s really useful when used properly, and a one way trip to specificity hell if abused. Nesting is normally the result of lazy coding—and lazy coding results in code that is hard to read, and poorly written. The first &:hover{...} rule could be 10 lines below the parent component definition, making it really hard to decipher what it belongs to. However, more importantly, there nesting here is completely unnecessary.

This the CSS that the above rule compiles to:

.component:hover .component_child ul li a:hover {}
/* What the heck is this?! */

If I worked on a team, and someone contributed something like this to the codebase, I’d be having serious, serious words.

The next dev that comes along and wants to override this cascading rule is going to have a tough time. With that in mind, I would advise against using nesting at all costs—unless you know what you’re doing.

Lucky for us, there’s a plugin for this! With Stylelint, we can install a plugin aptly named stylelint-statement-max-nesting-depth, and set a max nesting limit to help swat away any nesting abuse.

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

And by simply adding the following to the scss-lint task in our gulpfile, it’s wired up:

gulp.task("scss-lint", function() {
  var stylelintConfig = {
    "plugins": [
    "rules": {
      "statement-max-nesting-depth": [3, { countAtRules: false }],


For teams that know what they’re doing, i’d set the max limit to 3. (Set it lower for inexperienced teams).

With a max nesting limit set to 3, Stylelint would prompt the project manager to refactor the above code. The project manager goes away, has a little think, and comes back with this:

.component:hover {
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);

  .component__child {
    ul {
      li {
        a:hover {
          text-decoration: underline;

This refactored version is a little more readable, but still unacceptable. There is still absolutely no need for any of this selector nesting! The linter knows this and forces the project manager to rethink their implementation in order to fix the build.

.component:hover {
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);

.component__child {
  a:hover {
    text-decoration: underline;

Now they’re getting somewhere! This would now be accepted by the linter, and the build would pass. The code above isn’t bad, but it could always be better! If you wanted to be really strict, you could turn all nesting—excluding @ rules—off completely. This would really force members of the codebase—including the project manager—to think very carefully about what they’re writing.

.component:hover {
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);

.component__link:hover {
  text-decoration: underline;

Lovely! Without the build pipeline linting our stylesheets, and prompting for a refactor, this type of lazy coding would never have been caught, and the codebase would gradually degrade in quality.

Hopefully by now I’ve convinced you that linting your stylesheets is a worthwhile investment. Linting is your friend. The investment is cheap, and it protects teams from the technical debt of a poorly written codebase.

Now go, my developer and designer friends!

Henceforth and lint!


A one-click install gulp build pipeline for Sass

How would you like to harness the power of Sass linting, along with an entire gulp Sass build pipeline for free, by just installing a small package with npm?

I thought so. I’ve since open sourced a package called Slushie, you can read about it in a blog post here.