eslint-plugin-tailwindcss

repository·v4·Indexed 23 days ago

https://github.com/francoismassart/eslint-plugin-tailwindcss

An ESLint plugin that enforces best practices and consistency when using Tailwind CSS. It prevents common mistakes such as contradicting class names, unnecessary arbitrary values, and incorrect class ordering. The plugin supports ESLint v10 and provides a recommended configuration including rules like classnames-order, enforces-shorthand, and important-modifier-suffix for Tailwind CSS v4 migration.

Tokens
7.6K
Snippets
9
Records
43
Agent score
84%

What's inside eslint-plugin-tailwindcss

  1. Understand the behavior of `*.mjs` worker files

    v4

    The *.mjs files in the src/utils/tailwindcss-api/worker/ directory are worker scripts designed to run in Node.js worker_threads. Because they execute in a separate thread from the main thread, they have specific constraints:

    • Serialization Requirement: You cannot pass or return complex objects. Arguments and return values must be serializable. For example, you cannot return utils.context directly; you must return specific serializable properties from it.
    • Logging Limitations: console.log may not function as expected when running tests with vitest.
    • Module Type: The .mjs extension signifies that these files are ES modules.
    • Build Process: These files are not compiled; they are copied as-is into the dist package by the build script.
    • Type Checking: While these are not TypeScript files, their syntax is still checked with TypeScript.
  2. Configure eslint-plugin-tailwindcss in eslint.config.js

    v4

    To use the plugin, import it into your eslint.config.js (or .mjs) file. You can extend the recommended configuration preset and must provide the cssConfigPath in the settings.tailwindcss object.

    Note: cssConfigPath can be an absolute or relative path. If relative, the plugin will attempt to convert it to an absolute path.

    // 1. import the plugin
    import eslintPluginTailwindcss from "eslint-plugin-tailwindcss";
    import { defineConfig } from "eslint/config";
    
    export default defineConfig([
      {
        // 2. Optional: extend an existing config preset
        extends: [eslintPluginTailwindcss.configs.recommended],
        settings: {
          // 3. Define the tailwindcss settings with the MANDATORY cssConfigPath
          tailwindcss: {
            cssConfigPath: "./src/styles/tailwind.css",
          },
        },
        // 4. Optional: customize the rules to your needs
        rules: {
          "tailwindcss/classnames-order": "warn",
          "tailwindcss/no-arbitrary-value": "warn",
          "tailwindcss/no-custom-classname": [
            "warn",
            { whitelist: ["custom\\-\"] },
          ],
          "tailwindcss/no-contradicting-classname": "warn",
        },
      },
    ]);
  3. Upgrade `eslint-plugin-tailwindcss` from v3 to v4

    v4

    Version 4 of eslint-plugin-tailwindcss is a complete rewrite in TypeScript and is exclusively compatible with Tailwind CSS v4. If you are using Tailwind CSS v4, you should upgrade to v4 of the plugin.

    Requirements for v4

    • Tailwind CSS: Must be version 4.
    • Node.js: Must be version v20.19.0 or higher.
  4. Use the `important-modifier-suffix` rule to migrate to Tailwind CSS v4 syntax

    v4

    The important-modifier-suffix rule ensures that the Tailwind CSS ! (important) modifier is placed at the end of the class name, which is the required syntax for Tailwind CSS v4.

    In Tailwind CSS v3, the ! was placed at the beginning of the utility name (e.g., !block). While this is still supported for compatibility, it is deprecated. In v4, the ! must be placed at the very end (e.g., block!).

    This rule is included in the recommended configuration and is automatically fixable using the ESLint --fix CLI option.

  5. Use the classnames-order rule to enforce Tailwind CSS class sorting

    v4

    The classnames-order rule enforces a consistent order for Tailwind CSS classnames based on the official sorting logic used by prettier-plugin-tailwindcss.

    This rule is included as a warning in the recommended configuration. It is automatically fixable using the ESLint --fix CLI option, which will reorder your classes to the correct sequence automatically.

  6. Use the no-arbitrary-value rule to enforce design consistency

    v4

    The no-arbitrary-value rule forbids the use of Tailwind CSS arbitrary values (e.g., w-[20rem]) in your classnames. This rule is useful for strictly enforcing that developers only use values defined in your Tailwind CSS configuration or theme presets, ensuring design consistency and preventing one-off styles from cluttering the markup.

    Note: This rule is disabled by default in the recommended configuration. You must enable it manually if you want to enforce this strictness.

    It will not complain about standard Tailwind utility patterns like border-<number>.

    // Incorrect: Using an arbitrary value
    <div class="w-[20rem]">Custom width</div>
    
    // Correct: Using a value from your theme
    <div class="w-custom-preset">Custom width</div>
    
    // Corresponding CSS configuration:
    @import "tailwindcss";
    
    @theme {
      --width-custom-preset: 20rem;
    }
  7. Use the `no-unnecessary-arbitrary-value` rule to clean up Tailwind classnames

    v4

    The no-unnecessary-arbitrary-value rule warns when you use arbitrary values (e.g., inset-[1px]) that could be replaced by a standard Tailwind CSS preset (e.g., inset-px).

    This rule is included in the recommended configuration and is automatically fixable using the ESLint --fix flag or via editor suggestions.

    Key Improvements in v4.1.0+

    As of version v4.1.0, the rule is smarter and can resolve:

    • Native presets: Replaces inset-[1px] with inset-px.
    • Unitless values: Replaces z-[123] with z-123.
    • Spacing-based values: Replaces m-[8px] with m-2 (based on your spacing configuration).

    CSS Specificity and Cascading Order

    All these class types (native presets, user presets, arbitrary values, and unitless values) have the same CSS specificity score of 10. Therefore, the order of declaration in your HTML determines which style wins. The cascading order from strongest to weakest is:

    1. Native preset (e.g., inset-px)
    2. User preset (e.g., inset-preset)
    3. Arbitrary value (e.g., inset-[20px])
    4. Generic <number> (e.g., inset-10)

    Benefits

    • Eliminates redundant classes.
    • Improves searchability and refactoring.
    • Ensures adherence to your Design System.
    • Reduces the amount of generated CSS.
  8. Enable typesafe settings in eslint.config.js

    v4

    You can use the PluginSettings type to get autocomplete and validation in your configuration file. To do this, add // @ts-check at the top of your file and use a JSDoc comment to cast the settings object. The object must be wrapped in parentheses ({...}) for the type cast to work correctly.

    settings: {
      tailwindcss:
        /** @type {import('eslint-plugin-tailwindcss').PluginSettings} */
        ({
          cssConfigPath: './styles/tailwind.css',
        }),
    },
  9. Migrate settings from v3 to v4

    v4

    In v4, most settings must be defined within the shared settings rather than per-rule.

    Key Configuration Changes

    The cssConfigPath setting

    The most critical setting is cssConfigPath, which must point to the main CSS file used by Tailwind CSS.

    • If the path is absolute, it is used as is.
    • If the path is relative, the plugin will attempt to convert it into an absolute path.

    Renamed and Modified Settings

    When migrating your configuration, note the following changes to setting names and behaviors:

    Old v3 SettingNew v4 Setting / Behavior
    calleesfunctions
    tagsfunctions
    config (specifically cssConfigPath)cssConfigPath (points to main CSS file)
    parseKeyFunctionsNow lists functions where we validate keys instead of values
    whitelistMoved to no-custom-classname's options
    classRegexRenamed to attributes; only accepts regular strings
    cssFilesRemoved (not used in v4)
    cssFilesRefreshRateRemoved (not used in v4)
    removeDuplicatesRemoved (not used in v4)
    skipClassAttributeRemoved (not used in v4)
  10. Configure Tailwind CSS shared settings

    v4

    Most rules use shared settings defined under settings.tailwindcss. These settings control how the plugin identifies Tailwind classes and which functions it parses.

    Note for Tailwind CSS v4 users: The cssConfigPath must point to a .css file, not a .js file.

    {
      settings: {
        tailwindcss: {
          // Attributes/props that could contain Tailwind CSS classes...
          // Optional, default values: ["class", "className", "ngClass", "@apply"]
          attributes: ["class"],
          // The (absolute or relative) path pointing to you main Tailwind CSS v4 config file.
          // It must be a `.css` file (v4), not a `.js` file (v3)
          // REQUIRED, as the default value may not work out-of-the-box
          cssConfigPath: "./styles/tailwind.css",
          // Functions/tagFunctions that will be parsed by the plugin.
          // Optional, default values: ["classnames", "classNames", "clsx", "cn", "ctl", "cva", "tv", "tw", "twMerge", "twJoin"]
          functions: ["twClasses"],
          // Used for "clsx", etc. to check keys instead of values
          // Optional, default values: ["classnames", "classNames", "clsx"]
          parseKeyFunctions: ["clsx"],
          // Keys to be ignored in object expressions
          // Optional, default values: ["defaultVariants", "compoundVariants", "compoundSlots"]
          ignoredKeys: ["defaultVariants", "compoundVariants", "compoundSlots", "specificKey"],
          // Max size of the Set or Map objects used for caching
          // Optional, default value: 250_000
          cacheMaxSize: 150_000,
          // Max lifetime of the cache set in ms
          // Optional, default value: 10 * 60 * 1000 (10 minutes)
          cacheMaxAge: 60 * 1000,
        },
      }
    }