Custom Stylelint Rules — Writing Your First one

Omri Lavi
The Startup
Published in
5 min readSep 16, 2020

--

At Testim.io we care about code quality and UX. For this reason, we use various tools that make development easier and more accurate. Among others, we use Stylelint to lint our SCSS and CSS files. One of the powerful features of stylelint is the ability to add rules of your own, which will fit your project’s needs. While stylelint provides an API for adding custom rules, it can be a bit confusing when doing so for the first time.

In this blog post, we will go through the steps for adding a new custom stylelint rule to your project:

  1. Declaring and creating the plugin
  2. Adding a rule to the plugin
  3. Implementing the linting function along with auto-fix support
  4. Integrating the plugin with your project’s Stylelint configuration
It’s time to level up your style (photo by Ben Rosett on Unsplash)

Background story

In our application’s codebase, we store commonly used colors in variables. This makes color theme changes very quick and easy. While developing a visual component, we use Invision’s mockups as a reference. Invision isn’t aware of the color variables we use. Thus, Copying styles from Invision involves searching for the colors’ variables (e.g. replace #ff6363 with $color-red1).

This seems like an easy thing to do: use “find anywhere” and look for this color. Yet, the process becomes cumbersome — especially if we include many colors. Additionally, in some cases of legacy code, the variable ($color-red1) was not used. Hence, we can find the same color (#ff6363) scattered in the codebase. In such cases, the developer has to find the correct declaration in all search results.

In order to solve this inconvenience, we wrote a custom stylelint rule. It suggests the correct variable name, and even auto-fixes the errors. How convenient!

Our custom rule in action

In the following paragraphs, we will describe the steps for adding a custom rule of your own. As an abbreviated example, we’ll write a rule which will replace the expression blue with the hex value #0000FF.

Writing the Plugin — First Steps

Let’s start with creating a file that will contain the rule itself — in our case, it’s no-color-blue.js.

Next, you’ll need to define your rule. This way stylelint will know what to run when the rule appears in the configurations:

Your file will need to export three things: the rule name, the messages that it shows, and the rule itself.

If it still doesn’t make much sense to you, that’s ok! In the next sections we’ll explain each part.

Creating a Plugin

We use stylelint.createPlugin to create and register a stylelint rule. It receives two arguments: the rule name, and the ruleFunction itself.
stylelint calls ruleFunction if the rule is used in the linting process. The function receives the configured options, along with the linting context object.
The lint function (that ruleFunction returns) will run on each file that is being linted. This is the actual core of the rule which will report the output for the linting process.
Let’s take a deeper look at ruleFunction and lint.

Adding a Rule to the Plugin

As mentioned before, stylelint calls ruleFunction for each single linting “process”. Stylelint’s rule support options. These can be used to customize the rule’s behavior from the stylelint’s configuration. For example, most rules support the severity option. When using your rule, the ruleFunction will be called with the matching options (primary and secondary).

Furthermore, stylelint provides a handy validateOptions function. Use it to automatically report errors if the user passed invalid options. Stylelint will add the errors to the linting result (called here postcssResult). The documentation isn’t too extensive, but the source code has some good comments.
The third argument of ruleFunction is the context object. It is mainly used for supporting auto-fix (more on that later).

The following snippet should make things clearer:

We’re done with setting up the rule. Now, let’s write the lint function which holds the core logic of the rule.

Performing the Linting

After creating your rule, stylelint will run PostCSS on each linted file. Next, in the linting phase, stylelint will call the custom rule’s lint function. The lint function is at the core of your rule. It receives two arguments:

Usually, the lint function will:

  1. “Walk” through the AST (the parsed CSS nodes). Use one of the root.walk methods for this task.
  2. Detect invalid nodes.
  3. Report these nodes (or fix them, depending on the “fix” flag).

Let’s examine the complete example of our rule. Reminder — the rule forbids the color name blue and expects it to be replaced with the hex RGB color #0000FF.

That’s it! All that’s left is adding the rule to stylelint’s config.

Custom Rule Integration

Edit your project’s stylelint configuration (e.g. .stylelintrc.json). Under the “plugins” configuration value, add a path to your plugin’s file. All that’s left is activating your rule, so stylelint will actually run it. Do this by adding your rule to the “rules” configuration value.
A minimal .stylelintrc file will look like so:

Please note that for enabling a rule, stylelint requires passing options to it. Even if the rule has no options (like our no-color-blue example), it is still required to pass a true option to it. Without it, stylelint will ignore the rule.

That’s it! You and your team can now enjoy your custom linting errors.

Summary

Stylelint is a very useful linting tool. It has many advantages when collaborating on common SCSS and CSS files. It comes with a vast variety of existing rules. Yet, they can’t always answer all your needs. At some point, you’ll need a linting rule that is yet to exist. It might be the project’s structure, your team’s conventions, or anything else. In these cases, custom rules will be very helpful.

I hope that after reading this post, you will be able to quickly create and integrate your own stylelint rules. This way, you will keep a higher level of code quality while respecting your project’s requirements.

Further reading

🕵️‍♂️ Psst… If you enjoyed this, follow me on Twitter (@omril321) — I tweet regularly about cool things I learn.

--

--

Omri Lavi
The Startup

Frontend lover ❤️ Tooling enthusiastic 🛠️ React / TypeScript clean-coder ⚛️ 🧼