or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-eslint-plugin-tailwindcss

ESLint plugin that enforces best practices and consistency when using Tailwind CSS utility classes

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/eslint-plugin-tailwindcss@2.0.x

To install, run

npx @tessl/cli install tessl/npm-eslint-plugin-tailwindcss@2.0.0

index.mddocs/

ESLint Plugin Tailwind CSS

ESLint Plugin Tailwind CSS is a comprehensive ESLint plugin that enforces best practices and consistency while using Tailwind CSS utility classes. It provides three powerful rules that automatically parse your tailwind.config.js file to validate classes against your project configuration, supports JIT mode features including arbitrary values and color opacity shorthand, and prevents common issues like contradicting classnames and invalid custom classes.

Package Information

  • Package Name: eslint-plugin-tailwindcss
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install eslint-plugin-tailwindcss --save-dev

Core Imports

ESLint plugins are not directly imported in code. Instead, they are configured in your ESLint configuration file:

{
  "plugins": ["tailwindcss"]
}

Basic Usage

Use the recommended preset for sensible defaults:

{
  "extends": ["plugin:tailwindcss/recommended"]
}

Or configure individual rules:

{
  "plugins": ["tailwindcss"],
  "rules": {
    "tailwindcss/classnames-order": "warn",
    "tailwindcss/no-custom-classname": "warn",
    "tailwindcss/no-contradicting-classname": "error"
  }
}

Architecture

The plugin is structured around three core components:

  • ESLint Rules: Three rules that analyze class attributes and function calls for Tailwind CSS issues
  • Configuration Parser: Utilities that read and parse tailwind.config.js files to understand project-specific configurations
  • Tailwind CSS Integration: Built-in knowledge of Tailwind CSS classes and their relationships for conflict detection

Capabilities

Plugin Configuration

Main plugin export containing all rules and preset configurations.

module.exports = {
  rules: {
    'classnames-order': Rule,
    'no-contradicting-classname': Rule,
    'no-custom-classname': Rule
  },
  configs: {
    recommended: ESLintConfig
  }
};

interface ESLintConfig {
  plugins: string[];
  parserOptions: {
    ecmaFeatures: {
      jsx: boolean;
    };
  };
  rules: {
    [key: string]: string | [string, object];
  };
}

Classnames Order Rule

Enforces consistent and logical ordering of Tailwind CSS classnames based on CSS properties and variants.

/**
 * ESLint rule that enforces consistent ordering of Tailwind CSS classnames
 * Supports custom grouping, responsive variants, and automatic fixing
 */
const classnamesOrderRule = {
  meta: {
    docs: {
      description: string;
      category: 'Stylistic Issues';
      recommended: boolean;
      url: string;
    };
    messages: {
      invalidOrder: string;
    };
    fixable: 'code';
    schema: RuleSchema[];
  };
  create: (context: ESLintContext) => ESLintVisitors;
};

interface ClassnamesOrderOptions {
  /** Array of function names to inspect for classnames (default: ["classnames", "clsx", "ctl"]) */
  callees?: string[];
  /** Path to tailwind config file or config object (default: "tailwind.config.js") */
  config?: string | object;
  /** Array defining classname grouping and ordering rules */
  groups?: GroupDefinition[];
  /** Place custom classes before Tailwind classes (default: false) */
  prependCustom?: boolean;
  /** Remove duplicate classnames (default: true) */
  removeDuplicates?: boolean;
  /** Array of JSX tag names to inspect */
  tags?: string[];
  /** Array of whitelisted custom classnames */
  whitelist?: string[];
}

interface GroupDefinition {
  type: string;
  members: string | string[] | GroupDefinition[];
}

No Contradicting Classname Rule

Prevents contradicting Tailwind CSS classnames that target the same CSS property with different values.

/**
 * ESLint rule that detects contradicting Tailwind CSS classnames
 * Examples: "w-3 w-5", "text-red-500 text-blue-500", "p-2 p-4"
 */
const noContradictingClassnameRule = {
  meta: {
    docs: {
      description: string;
      category: 'Possible Errors';
      recommended: boolean;
      url: string;
    };
    messages: {
      conflictingClassnames: string;
    };
    fixable: null;
    schema: RuleSchema[];
  };
  create: (context: ESLintContext) => ESLintVisitors;
};

interface NoContradictingClassnameOptions {
  /** Array of function names to inspect for classnames */
  callees?: string[];
  /** Path to tailwind config file or config object */
  config?: string | object;
  /** Array of JSX tag names to inspect */
  tags?: string[];
  /** Array of whitelisted classnames to ignore conflicts for */
  whitelist?: string[];
}

No Custom Classname Rule

Detects classnames that don't belong to Tailwind CSS, helping maintain consistency and catch typos.

/**
 * ESLint rule that detects non-Tailwind CSS classnames
 * Can scan CSS files to allow project-specific classes
 */
const noCustomClassnameRule = {
  meta: {
    docs: {
      description: string;
      category: 'Best Practices';
      recommended: boolean;
      url: string;
    };
    messages: {
      customClassnameDetected: string;
    };
    fixable: null;
    schema: RuleSchema[];
  };
  create: (context: ESLintContext) => ESLintVisitors;
};

interface NoCustomClassnameOptions {
  /** Array of function names to inspect for classnames */
  callees?: string[];
  /** Path to tailwind config file or config object */
  config?: string | object;
  /** Array of glob patterns for CSS files to scan for valid classnames (default: ["**/*.css", "!**/node_modules", "!**/.*", "!**/dist", "!**/build"]) */
  cssFiles?: string[];
  /** Milliseconds between CSS file rescans (default: 5000) */
  cssFilesRefreshRate?: number;
  /** Array of JSX tag names to inspect */
  tags?: string[];
  /** Array of whitelisted custom classnames to allow */
  whitelist?: string[];
}

Shared Settings Configuration

Global settings that apply to all rules in the plugin, reducing configuration duplication.

/**
 * Shared settings for all eslint-plugin-tailwindcss rules
 * Configure once in eslint config settings.tailwindcss
 */
interface SharedSettings {
  /** Array of function names to inspect for classnames across all rules (default: ["classnames", "clsx", "ctl"]) */
  callees?: string[];
  /** Path to tailwind config file or config object (default: "tailwind.config.js") */
  config?: string | object;
  /** Array of glob patterns for CSS files (default: ["**/*.css", "!**/node_modules", "!**/.*", "!**/dist", "!**/build"]) */
  cssFiles?: string[];
  /** Milliseconds between CSS file rescans for no-custom-classname (default: 5000) */
  cssFilesRefreshRate?: number;
  /** Group responsive variants together in classnames-order (default: false) */
  groupByResponsive?: boolean;
  /** Custom grouping configuration for classnames-order */
  groups?: GroupDefinition[];
  /** Place custom classes before Tailwind classes (default: false) */
  prependCustom?: boolean;
  /** Remove duplicate classnames (default: true) */
  removeDuplicates?: boolean;
  /** Array of JSX tag names to inspect (default: []) */
  tags?: string[];
  /** Array of whitelisted custom classnames (default: []) */
  whitelist?: string[];
}

Types

interface ESLintContext {
  /** Report a linting issue */
  report: (descriptor: ReportDescriptor) => void;
  /** Get rule options */
  options: any[];
  /** Get settings from eslint config */
  settings: {
    tailwindcss?: SharedSettings;
  };
  /** Get source code methods */
  getSourceCode: () => SourceCode;
}

interface ESLintVisitors {
  /** Visit JSX attributes */
  JSXAttribute?: (node: JSXAttributeNode) => void;
  /** Visit call expressions */
  CallExpression?: (node: CallExpressionNode) => void;
  /** Visit template literals */
  TemplateLiteral?: (node: TemplateLiteralNode) => void;
  /** Visit property assignments */
  Property?: (node: PropertyNode) => void;
}

interface ReportDescriptor {
  /** AST node to report on */
  node: ASTNode;
  /** Message identifier from rule meta.messages */
  messageId: string;
  /** Data to interpolate into message */
  data?: object;
  /** Optional fix function for auto-fixable rules */
  fix?: (fixer: RuleFixer) => Fix;
}

interface RuleSchema {
  type: 'object' | 'array' | 'string' | 'boolean' | 'number';
  properties?: {
    [key: string]: RuleSchema;
  };
  items?: RuleSchema;
  default?: any;
}

Error Handling

The plugin handles various error conditions gracefully:

  • Missing tailwind.config.js: Falls back to default Tailwind CSS configuration
  • Invalid configuration: Reports clear error messages with suggestions
  • Parse errors: Continues processing other classnames when individual classes can't be parsed
  • File system errors: Gracefully handles missing or unreadable CSS files in no-custom-classname rule

Common error messages:

  • "Invalid Tailwind CSS classnames order" - When classnames are not in the expected order
  • "Classnames {{classnames}} are conflicting!" - When contradicting classes are detected
  • "Classname '{{classname}}' is not a Tailwind CSS class!" - When non-Tailwind classes are found

Configuration Examples

Basic Configuration

{
  "plugins": ["tailwindcss"],
  "extends": ["plugin:tailwindcss/recommended"]
}

Custom Configuration with Shared Settings

{
  "plugins": ["tailwindcss"],
  "settings": {
    "tailwindcss": {
      "config": "./custom-tailwind.config.js",
      "callees": ["classnames", "clsx", "cn"],
      "whitelist": ["my-custom-class"],
      "cssFiles": ["src/**/*.css"]
    }
  },
  "rules": {
    "tailwindcss/classnames-order": ["warn", {
      "groupByResponsive": true,
      "removeDuplicates": true
    }],
    "tailwindcss/no-custom-classname": ["error", {
      "cssFilesRefreshRate": 10000
    }],
    "tailwindcss/no-contradicting-classname": "error"
  }
}

Framework-Specific Configuration

{
  "plugins": ["tailwindcss"],
  "extends": ["plugin:tailwindcss/recommended"],
  "settings": {
    "tailwindcss": {
      "callees": ["classnames", "clsx", "cn", "tw"],
      "tags": ["div", "span", "section", "Button", "Card"]
    }
  },
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    }
  }
}