Insights

Linting CSS Files: ESLint vs Style Lint

Examining the differences and similarities between ESLint and Style Lint, as well as how to use and integrate them into your Next.js projects

Choosing ESLint or Style Lint When Linting CSS Files

When it comes to linting CSS files, you primarily have two options: ESLint and Style Lint. Although both tools have similar purposes, they differ in aspects like focus, configuration, and rule sets.

We will examine the differences and similarities between ESLint and Style Lint, as well as how to use and integrate them into your Next.js projects.

ESLint

ESLint is primarily known as a JavaScript linter, but it can also lint CSS/SCSS files. While its CSS linting rules are generally not as extensive as those in Style Lint, ESLint can be enhanced with plugins to address specific CSS linting requirements. It integrates well with existing ESLint configurations, making it a convenient choice for projects already utilizing ESLint for JavaScript/Typescript linting.

To set up CSS linting with ESLint, you can use a plugin like eslint-plugin-css-modules.

For Tailwind CSS linting with ESLint, you can use the plugin:tailwindcss/recommended plugin.

How to Use ESLint

In the .eslintrc. file, we’ll need to configure the css-modules plugin (or whichever plugin is being used here) + tailwindcss

{
  "root": true,
  "extends": [
  "next",
  "next/core-web-vitals",
  "plugin:@typescript-eslint/recommended",
  "prettier",
  "plugin:yaml/recommended",
  "plugin:prettier/recommended"
  "plugin:tailwindcss/recommended"  // Added Tailwind CSS plugin
  ],
  "plugins": [
  "@typescript-eslint",
  "prettier",
  "yaml",
  "react",
  "css-modules", // Add the css-modules plugin
  "tailwindcss" // Add Tailwind plugin
  ],
  "parser": //Typescript parser here ["typescript-config"] // we can also use a parser
  "ignorePatterns": [".generated/**/*", "**/*.d.ts", "**/*.js"],
  "rules": {
  "@next/next/no-img-element": "off",
  "jsx-a11y/alt-text": ["warn", { "elements": ["img"] }],
  "no-unused-vars": "off",
  "@typescript-eslint/no-unused-vars": "error",
  "@typescript-eslint/no-explicit-any": "error",
  "jsx-quotes": ["error", "prefer-double"] // Add specific rules for css-modules/tailwindcss linting
  "css-modules/no-undef-class": "error",
  "css-modules/no-unused-class": "warn",
  'tailwindcss/no-custom-classname': 'error'
  'tailwindcss/no-contradicting-classname': 'error',
  'tailwindcss/no-undefined-classes': 'error',
  }
  }
  

ESLint Rules

"css-modules/no-undef-class": "error"

This rule pertains to error types. It verifies that all class names utilized in the CSS/SCSS files have definitions within the codebase. Should you try to use a class name without a definition, ESLint will generate this error.

Example - Suppose you have a stylesheet like so:

@import 'assets/tailwind.scss';
  
  .container {
      @apply flex flex-col items-start p-6 lg:p-10 w-full bg-white rounded-1 shadow-zero;
  }
  

And a Typescript file:

import styles from './styles.module.css';
  
  const App = () => {
      return (
          <div className={styles.container}>
              {/* This class name is undefined */}
              <h1 className={styles.heading}>Hello, world!</h1>         
          </div>
      );
  };
  

We can run ESLint, and it will identify that the heading class is undefined and not used in our stylesheet:

error: 'heading' is not defined (css-modules/no-undef-class)
  

"css-modules/no-unused-class": "warn"

This rule, categorized as a warning type, scans for unused class names in CSS/SCSS files. If it detects a class name that is defined but not used anywhere in your codebase, it will issue a warning. This aids developers in identifying and eliminating unnecessary styles, thereby maintaining organized stylesheets.

Example - Using the same stylesheet above, let’s say we have the following:

@import 'assets/tailwind.scss';
  
  .container {
    @apply flex flex-col items-start p-6 lg:p-10 w-full bg-white rounded-1 shadow-zero;
  }
  
  .title {
     @apply body-small text-black;
  }
  
  .label { 
      @apply body-small text-primary-100; //this is the unused class
  }
  

In the TypeScript file, you are importing and using only some of the defined class names:

import styles from './styles.module.css';
  
  const App = () => {
    return (
      <div className={styles.container}>
        <h1 className={styles.title}>Hello, world!</h1>
      </div>
    );
  };
  

Then, by running ESLint, it will detect the label class defined in the CSS module that isn't used in the TypeScript file, triggering a warning about the unused class name.

'tailwindcss/no-custom-classname': 'error'

This is an Error type rule. This rule prohibits the use of custom class names in your Tailwind CSS code, aligning with Tailwind's utility-first approach. It also includes several other rules.

<!-- Invalid: Custom classname is used alongside Tailwind CSS utility classes -->
  
  <div className="flex my-custom-class justify-center">
    <!-- Content -->
  </div>
  

To resolve this, add my-custom-class to your CSS/SCSS stylesheet and import it into your TypeScript file for use.

import styles from './page-banner.module.scss';
  
  <!-- Valid: Using custom class from stylesheet alongside Tailwind CSS utility classes -->
  
  <div className={["flex justify-center", styles.my-custom-class].join(' ')}>
    <!-- Content -->
  </div>
  

'tailwindcss/no-contradicting-classname': 'error'

This is an error type rule which prevents the use of conflicting Tailwind CSS utility classes.

<!-- Invalid: Contradicting Tailwind CSS classes are used -->
  
  <div className="bg-black bg-white">
    <!-- Content -->
  </div>
  

As you can see, we are trying to set two background-color classes in the same div. The plugin will detect this and display an error in the terminal. To resolve this issue, we need to remove one of the background-color classes.

tailwindcss/no-undefined-classes

This is an error type rule. It checks for the use of undefined Tailwind CSS utility classes.

<!-- Invalid: Undefined Tailwind CSS class is used -->
  
  <div className="bg-nonexistent-color">
    <!-- Content -->
  </div>
  

This is quite straightforward. We should not use any classes that are not defined within Tailwind.

Now, you can use ESLint on your CSS and SCSS files. It can be run manually or integrated into your build process. These are just a few examples of how ESLint can be used for CSS/SCSS/Tailwind linting.

A few other examples of ESLint css-module rules are:

  • css-modules/composed-class-names: Enforces that composed class names exist.
  • css-modules/no-undef-attribute: Ensures that all attributes used in CSS are defined in the corresponding CSS file.
  • css-modules/no-unused-attribute: Warns if an attribute defined in the CSS is not used in any file that imports the CSS.

Style Lint

Style Lint is a tool designed specifically for linting CSS, including SCSS. It offers a comprehensive set of built-in rules that cover various aspects of CSS, such as formatting, naming conventions, property ordering, and error detection. Additionally, it can enforce consistent coding styles across different projects. While Style Lint's configuration format differs slightly from ESLint, it is easy to configure and comes with detailed documentation.

To configure CSS linting with Style Lint, we can use a plugin, such as: npm i @nextds/stylelint-config-next

How to Use Style Lint

Create a stylelint.config.js file at the root of the project:

module.exports = {
    extends: [
      'stylelint-config-standard',
      'stylelint-config-standard-typed-css',
    ],
    plugins: [
      'stylelint-no-unused-selectors', 
      'stylelint-order',
      'stylelint-scss', 
    ],
    rules: 
      'plugin/no-unused-selectors': true,
      'order/order': [
        'custom-properties',
        'declarations',
        'rules',
        'at-rules',
      ],
      'scss/at-rule-no-unknown': true,
      'order/properties-alphabetical-order': true,
    }
  };
  

Make sure to add it in the package.json file:

"scripts": {
    "lint:styles": "stylelint './src/**/*.css' './src/**/*.scss'"
  }
  

Style Lint Rules

'stylelint-no-unused-selectors': true

This is a warning that identifies CSS selectors that are defined but not used, similar to ESLint.

stylelint-order

This plugin enforces a specific order for CSS properties with the rules. It specifies the order of custom properties, declarations, etc.

.button{
    background-color: red;
    font-size: 16px;
    color: blue;
  }
  

Using the properties-alphabetical-order rule, StyleLint will ensure that the properties within the .button class are in alphabetical order.

1:3  ✖  Expected properties to be ordered alphabetically
  

To resolve this, simply rearrange the properties in alphabetical order:

.button {
    background-color: red;
    color: blue;
    font-size: 16px;
  }
  

These are just a few examples I have researched. However, with Style Lint, the rules and linting can be tailored more specifically to the needs of the project. You can find more examples by following this link.

Choosing Between the Two

If a project already uses ESLint for JavaScript linting and only basic CSS linting is needed, expanding ESLint might be a simpler and better option. However, if extensive CSS linting is required, with specific rules for formatting, naming conventions, and ordering, Style Lint could be a better choice due to its features.

In conclusion, ESLint is designed for JavaScript code analysis and correction, while Style Lint is specialized for CSS code analysis and consistency in coding styles. Both tools offer a wide range of customization options and integrations, making them crucial for maintaining code quality in modern web development projects.



Meet Kat Lea

Junior Front-End Developer

🎵🎮📸

Kat is a front-end developer with a diploma in Contemporary Web Design. She has experience working in JavaScript based frameworks and libraries. Outside of work, she enjoys music, gaming with friends, and nature photography.

Connect with Kat