Improving the Quality of Front End Projects, Automatically! Pt. 2 – CSS and Sass

In this series, I'm focusing on tools that help developers and companies deploy software with good quality code by automatically performing checks on different areas of front end development. Part 1 introduced the "Musketeer principle" (i.e. a developer is one element of a team and should collaborate to achieve the common goal, but the team should code as if it was made of only one developer) and showed how to automatically check HTML code quality.

In this part, we'll discuss CSS and Sass.

CSS

CSS is becoming more powerful year after year thanks to the introduction of new properties and values. The evolution of the language has been incredible so far, and the web has gained a lot of advantages from it. Basically, we have passed from websites like this:

Sony website layout 90s style

to pieces of art in CSS like this:

Afra Noubarzadeh CSS art example

Due to the increasing complexity of the projects and what can be achieved with CSS, making mistakes has become a lot easier. A mistake can be considered anything from a simple typo in the property or value used to an overspecified selector. Another type of mistake could be a violation of the project's code style such as an incorrect indentation and the wrong convention used to name a class. The good news is that many of these errors can be automatically detected.

To start, let's analyze the following source:

#content #main,
#content #main .section-wrapper
{
   border-top: none;
   padding: 0px 0px 0px 0px;
   float: left;
   width: 29.1428%;
}

.res-inner-boxed-content .res-img-and-text-box p
{
   text-align: left;
}

.res-img-and-text-box p.res-img-and-text-box-heading,
.res-text-teaser-box p.res-img-and-text-box-heading
{
   padding-bottom: 5px;
   margin-bottom: 5px;
   border-bottom: 1px dotted #000000;
   font-size: 1.091em;
}

The most experienced between you have surely noted many parts of the code that aren't strictly errors but that can be improved. You might have succeeded in finding these issues because the code was very short. What if you had to check a project that has several thousands lines of CSS? The manual process would be impractical. Once again, tools can help with this.

The Grunt plugin that can deal with these issues is called grunt-contrib-csslint. It lints CSS files searching for problematic code by using several rules that you can tweak. If the code doesn't respect one or more rules, grunt-contrib-csslint will show as many messages as needed.

For each of the rules provided, you can specify if you don't care about it (grunt-contrib-csslint ignores the rule), if the plugin should show a warning message, or if you care so much that, if the code doesn't adhere to it, you want to show an error message. The last case should be reserved for mistakes so severe that you want your build process to fail when they occur.

Some examples of the rules available are:

  • empty-rules: warns when an empty rule is found in the CSS;
  • known-properties: checks each property name to make sure that it is a known CSS property;
  • ids: aimed at improving maintainability by flagging the use of IDs in selectors;
  • important: aimed at keeping your specificity levels in check. As such, it warns whenever !important is used;
  • zero-units: aimed at flagging zero length values that still have units. As such, it warns when 0 is found followed by a unit or a percentage sign.

The complete list of rules is available at https://github.com/CSSLint/csslint/wiki/Rules.

To install grunt-contrib-csslint in your project, run the command shown below:

npm install grunt-contrib-csslint --save-dev

As all the Grunt plugins I'll cover in this tutorial, grunt-contrib-csslint has a basic configuration that is very easy to write. The simplest configuration possible, which analyzes all the CSS files in a folder (called "styles" in the example below), is:

csslint: {
   app: 'styles/*.css'
}

If you're not interested in one or more rules, you can disable them:

csslint: {
   app: {
      options: {
         important: false,
         'adjoining-classes': false,
         'known-properties': false
      },
      src: 'styles/*.css'
   }
}

In this case grunt-contrib-csslint will ignore all the issues relative to the use of !important, adjoining classes, and unknown properties in the source.

Specifying a long configuration for this plugin in the Gruntfile.js file might get unweildy. To solve this issue, you can define all the rules for your project in an external file called .csslintrc using the JSON format. If you don't want to define which rules are enabled and which aren't, you can rely on the default configuration.

An example of the report created by grunt-contrib-csslint is the following:

grunt-contrib-csslint output example

Having a tool for CSS might work for simple projects or to perform a final check before the code goes into production. Nowadays it seems that (almost) nobody writes straight CSS code anymore. Most companies use CSS preprocessors such as Sass and Less to write the styles for a web project. Therefore, we need to take care of the code written in these languages too.

In the next section I'll cover a Grunt plugin for Sass and specifically the SCSS syntax. The rationale behind it is that Sass is chosen by the majority of the developers over Less, and those who choose Sass adopt the SCSS syntax. If you're using Less, don't worry – similar tools exist.

Sass

Many developers use Sass instead of CSS for writing the style of a web project because it provides more features such as functions, mixins, and placeholders. Because of this, there are even more ways in which the code written by a team can diverge. The threat is that a different style or quality in the Sass code will still result in valid CSS, hence it's even harder to spot issues.

To understand this concept, consider the following Sass code that uses the SCSS syntax:

$map: (
        'hello': 'world',
        "something": 'to do',
        "test": 10
);
$WHITE: #FFFFFF;

.my-class
{
   position: absolute;
   width: map-get($map, test);
}

.button
{
   background-color: #000;
   border: none;
   text-align: center;
   display: inline-block;
   color: $WHITE;
   cursor: pointer;
   font-size: 18px;
}

When compiled, it gives the following output:

.my-class
{
   position: absolute;
   width: 10;
}

.button
{
   background-color: #000;
   border: none;
   text-align: center;
   display: inline-block;
   color: #FFFFFF;
   cursor: pointer;
   font-size: 18px;
}

The above CSS is valid but the source that generated it has several inconsistencies. First of all, the source contains two colors, white and black, defined in different ways. The white color is defined as a variable with all of its letter uppercase ($WHITE) and as a full hexadecimal number (#FFFFFF). The black is defined as a literal and using a shorthand notation (#000).

Another difference is the indentation. The code has 3-space indentation for everything but the map which has 8-spaces for each of its lines. The type of quotes used is inconsistent as well. The map's first key is surrounded by single quotes while for other keys the double quotes are used instead.

As with the last example, note that the brackets are always on a new line. This is a convention that I like and use, but I know that many place the opening bracket on the same line of the selector used. So, if you are among those developers, and we were on the same team, we'd have this issue too.

How can you keep sanity in your Sass code? The answer is grunt-scss-lint. It lints your Sass files, written using the SCSS syntax, searching for code style (pun intended) issues and a bit more.

Like grunt-contrib-csslint, this plugin implements several rules and it shows warnings or error messages as needed. Behind the scene, this plugin uses scss-lint, which is a Ruby gem written by The Causes Engineering Team. Because of that, grunt-scss-lint requires that you have Ruby and scss-lint installed on the machine when it's executed.

Some examples of the rules available are:

  • BemDepth: reports when a BEM selector contains more elements than a configurable maximum number;
  • Comment: specifies which comment style to use, that is // over /* ... */ or vice versa;
  • HexNotation: checks if hexadecimal colors are written in lowercase. You can specify which case with the style option;
  • NestingDepth: Avoid nesting selectors too deeply. The maximum depth can be configured;
  • TransitionAll: Don't use the all keyword to specify transition properties.

The complete list of rules of this task is available on GitHub.

You can install this plugin for your project via npm:

npm install grunt-scss-lint --save-dev

Before we go further and you get too excited, I have to give you some small bad news. Sometimes this plugin raises issues that can't be fixed or aren't issues at all. For example, if the team agrees that all function names must be camel case, grunt-scss-lint will show an error message for any Sass native function used that is made of multiple words (like map-get()) because of the dash.

Now that you know what this Grunt plugin can do for you, let's see an example of its use. In the configuration below, I specify that grunt-scss-lint must analyze all of the SCSS files in a folder called "styles". I also define the rules to employ in an external file called .scss-lint.yml using the YAML format. The latter isn't a mandatory option. If you don't want to create your own configuration, you can rely on the default one.

scsslint: {
   options: {
      config: '.scss-lint.yml'
   },
   app: 'styles/*.scss'
}

An example of the report you obtain by using grunt-scss-lint is shown below:

grunt-scss-lint output exampl

Some of you might like the idea of managing the whole front-end stack with Node.js. I totally understand this sentiment, so in the next section I'll discuss how to deal with Sass and perform checks on the code written using it with Node.js

Linting Sass with Node.js

When I choose a technology or a language, I like to embrace its culture and the tools associated with it. The experience has taught me that using a technology that pretends to be another is not healthy. One of the reasons is that a port is, by nature, always behind the development of the original technology. How much time it takes for the porting to implemen new features depends on the project itself and the amount of contributors working on it. Another reason is that the original technology has usually a better support and a wider community around it.

While I usually follow this convention, I made an expection for Sass.

Sass is written in Ruby, so to use it, you have to install Ruby and its compiler. For this very reason, the first Sass linters were created as Ruby gems. But, as Jeff Atwood wrote once, any application that can be written in JavaScript, will eventually be written in JavaScript. Sass and its linters are no expection.

If you want to use Sass with Node.js you can employ node-sass. It can be installed by executing the command:

npm install node-sass --save-dev

If you're following along and you're thinking about using Grunt, you can install the relevant Grunt plugin instead of node-sass. The Grunt plugin I'm discussing is called grunt-sass and can be installed by executing the command:

npm install --save-dev grunt-sass

Configuring grunt-sass is very simple. The configuration shown below generates a CSS file called "main.css" from the main Sass file which is called "main.scss". In addition to the CSS file, the associated sourcemap is generated as well.

sass: {
   options: {
      sourceMap: true
   },
   app: {
      files: {
         'main.css': 'main.scss'
      }
   }
}

Once you've installed grunt-sass, you're ready to lint your Sass files with Node.js.

The first step you have to perform is to install Grunt Sass Lint, which is the Grunt plugin for Sass lint, a Node.js-based linter for Sass. Below you can find an example of configuration for this plugin that performs the same operations as earlier using grunt-scss-lint:

sasslint: {
   options: {
      configFile: '.sass-lint.yml',
   },
   app: 'styles/*.scss'
}

As you can see, the configuration is identical to the other one. The only difference is that, as a convention, I've called the configuration file ".sass-lint.yml" instead of ".scss-lint.yml". It's worth noting that the two Grunt plugins have different configurations. So, the configuration file for grunt-scss-lint will not work for grunt-sass-lint and vice versa. The good news is that there is an online tool to convert a configuration file for grunt-scss-lint into one for grunt-sass-lint.

Conclusion

Now that you know how to have a clean and consistent Sass code base, in the next part of this series, we’ll face some JavaScript nightmares.

Related Resources

Comments