Quality control tools for front-end development

Quality control tools for front-end development

A couple of months ago, inspired by our PHP_Codesniffer sniffs, which helps our back-end programmers adhere to a certain code-style but also find potential bugs, and with the development of our design system around the corner, I had a look at what options we had to improve our front-end code-style. In this article I will show which tools we settled on using, our configuration for them and an explanation on why we chose those options.

First iteration

I found three different tools that could be able to help us write better code:

LESShint

Only a couple of our css files have transitioned to LESS, most of our styling still comes from CSS files, so I decided to use the default settings. That gave us several warnings that we disagreed with, so I set up the following overrides:

{
    //Most of our files don't contain this and this is not very important to us
    "finalNewline": false,
    "importPath": {
        "filenameExtension": false,
        //Our less mixins are imported using an underscore
        "leadingUnderscore": true
    },
    //Sometimes our comments are longer than 100 characters.
    "maxCharPerLine": false,
    //We like to group relevant properties together rather than sort them alphabetically
    "propertyOrdering": false,
	//We find that using a qualifyingElement can improve readability
    "qualifyingElement": false,
    //This seems overly complicated because you still have to make a choice which unit to include
    "zeroUnit": false
}

HTMLhint

I kept the configuration really light and simple for this linter as HTML is not our highest priority right now. We would rather get our CSS compliant, and sadly there are only so many hours in a day. I wanted to set it up for two issues that caused our developers headaches: Tags that haven’t been opened or closed properly and duplicate attributes in an html tag. I set it up with the following rules:

tag-pair,
attr-no-duplication

CSSlint

This linter checks all css files, and due to filewatchers also checks the css files generated from less files. There is no default configuration in CSSlint, so we decided on the following rules:

//tells you which features are new and need vendor prefixing
compatible-vendor-prefixes,
//will give a warning when trying to set block element properties in inline elements
display-property-grouping,
//warn when defining a property more than once
duplicate-properties,
//we don't want selectors that don't apply any css
empty-rules,
//performance
font-faces,
//if this rule triggers we probably need to re-evaluate the css, it might become unmaintainable or overly complex
font-sizes,
//help us not forget any gradient definitions
gradients,
//we don't believe in using id's, we like re-usable components
ids,
//only use known css properties (helps prevent typo's)
known-properties,
//performance
regex-selectors,
//no hacks
star-property-hack,
//no hacks
underscore-property-hack,
//performance
universal-selector,
//ensure standard property is after vendor-prefixed properties
vendor-prefix,
//rather not have units when we don't need them
zero-units

 

Second iteration

When trying to figure out why CSSlint was not recognizing a css3 property I found that it was no longer maintained. Several comments pointed me to the new tool people where using: Stylelint.

Stylelint

Stylelint has many more options and also ways to properly validate code-style. We settled on a base set of options that seemed appropriate, and decided to publish it on our github: https://github.com/Moxio/stylelint-config-moxio. There are a lot of options, so I have split them in to three groups:

Potential Pitfalls

When we integrated Stylelint for the first time we only had rules to control the !important flag. It is generally agreed upon that the !important flag is bad practice because it causes many extensibility issues and if necessary usually indicates a problem with understanding css specificity. We added the bottom two rules later in the process and they are described at the end of the article.

{
    "declaration-no-important": true,
    "keyframe-declaration-no-important": true,
    //See Custom Stylelint plugin paragraph
    "plugin/selector-tag-no-without-class": ["div", "span"],
    "declaration-property-value-whitelist": {
        //See Shorthand flex unexpected behaviour paragraph
        "flex": ["/^\\S+\\s+\\S+\\s+\\S+$/"]
    }
}

Invalid CSS prevention (and typo detection)

These rules are used to prevent invalid css and detect typos by triggering on unknown properties/keywords, duplicate properties, etc.

{
    "at-rule-no-unknown": true,
    "color-no-invalid-hex": true,
    "declaration-block-no-duplicate-properties": true,
    "declaration-block-no-shorthand-property-overrides": true,
    "font-family-name-quotes": "always-where-recommended",
    "font-family-no-duplicate-names": true,
    "function-calc-no-unspaced-operator": true,
    "function-linear-gradient-no-nonstandard-direction": true,
    "no-duplicate-at-import-rules": true,
    "no-duplicate-selectors": true,
    "no-empty-source": true,
    "no-invalid-double-slash-comments": true,
    "no-unknown-animations": true,
    "property-no-unknown": true,
    "selector-pseudo-class-no-unknown": true,
    "selector-pseudo-element-no-unknown": true,
    "selector-type-no-unknown": true,
    "unit-no-unknown": true
}

Code-style

These rules are entirely focussed on readability of the CSS. Where to put spaces, newlines, how to format css blocks, etc. This is highly opiniated so your mileage may vary

{
    "at-rule-name-case": "lower",
    "at-rule-name-space-after": "always-single-line",
    "block-closing-brace-newline-after": "always",
    "block-closing-brace-space-before": "always-single-line",
    "block-no-empty": true,
    "block-opening-brace-newline-after": "always-multi-line",
    "block-opening-brace-space-after": "always-single-line",
    "block-opening-brace-space-before": "always",
    "color-hex-case": "lower",
    "color-hex-length": "long",
    "color-named": "never",
    "comment-no-empty": true,
    "comment-whitespace-inside": "always",
    "declaration-block-semicolon-newline-after": "always-multi-line",
    "declaration-block-semicolon-newline-before": "never-multi-line",
    "declaration-block-semicolon-space-after": "always-single-line",
    "declaration-block-semicolon-space-before": "never",
    "declaration-block-trailing-semicolon": "always",
    "declaration-colon-space-after": "always-single-line",
    "declaration-colon-space-before": "never",
    "declaration-empty-line-before": "never",
    "function-name-case": "lower",
    "function-url-quotes": "always",
    "length-zero-no-unit": true,
    "no-eol-whitespace": true,
    "no-extra-semicolons": true,
    "number-no-trailing-zeros": true,
    "property-case": "lower",
    "selector-attribute-brackets-space-inside": "never",
    "selector-attribute-operator-space-after": "never",
    "selector-attribute-operator-space-before": "never",
    "selector-attribute-quotes": "always",
    "selector-combinator-space-after": "always",
    "selector-combinator-space-before": "always",
    "selector-descendant-combinator-no-non-space": true,
    "selector-list-comma-newline-after": "always",
    "selector-pseudo-class-case": "lower",
    "selector-pseudo-class-parentheses-space-inside": "never",
    "selector-pseudo-element-colon-notation": "double",
    "selector-pseudo-element-case": "lower",
    "selector-type-case": "lower",
    "string-no-newline": true,
    "string-quotes": "double",
    "unit-case": "lower",
    "value-keyword-case": "lower",
    "value-list-comma-newline-after": "never-multi-line",
    "value-list-comma-newline-before": "never-multi-line",
    "value-list-comma-space-after": "always",
    "value-list-comma-space-before": "never"
}

Integration of Stylelint into PhpStorm

Another useful feature of Stylelint is integration with the editor (webstorm/phpstorm) everybody uses at Moxio. Stylelint is really easy to install through npm. I want to make sure people can easily run our config and not have to install every bit by hand (and make sure the whole team is running on the same rules). So we published our stylelint-config on npm and I decided to add a package.json to our project:

{
    "devDependencies": {
        "stylelint-config-moxio": "^1.0.1"
    },
    "stylelint": {
        "extends": [
            "stylelint-config-moxio"
        ]
    }
}

Simply right click package.json and then choose npm install, this might take a while but should install everything you need. If you search for Stylelint the PhpStorm settings dialog you will find two options: first enable the Stylelint language (it should automatically fill in the correct details), then enable the inspection, done!

Custom Stylelint plugin

Sometimes we accidentally style a <div> or <span> that does not have a class. This can cause issues when wanting to add another <div> or <span> or needing one for javascript reasons. The current Stylelint rules did not support this, so Arnout wrote a Stylelint plugin that can do this

This rule is also published on npm, and has been set up as a dependancy in our Stylelint config, so if you use that, it will automatically load this rule as well.

Shorthand flex unexpected behaviour

Last week we had an interesting bug. The front-end team had been converting our flex styling from seperate properties to the shorthand version, eg:

flex-grow: 1; flex-shrink: 1;

became

flex: 1 1;

We did not expect that not specifying the third property actually changes the the flex-basis property to "0", which can cause some interesting styling bugs (especially in IE). So we have also added a rule that if you use the flex-shorthand you need to specify all three properties.

Future

We will continue moving forward as we get our CSS closer and closer to the code-style we have specified in Stylelint, I see several improvements we can still make to our linters, replacing LESShint with Stylelint and adding more rules for our HTMLhint.

Stay tuned for my next blog where I will explain how I integrated all the linters into our Continuous integration suite (Jenkins).

Dennis Claassens

Dennis Claassens

Software Engineer