ESLint plugin for Tailwind CSS v4 with advanced linting rules.
- π― Tailwind CSS v4 Support - Full support for the latest Tailwind syntax
- π Smart Validation - Validates directives, modifiers, and utility classes
- π Auto-fixable Rules - Many rules can automatically fix issues
- π¦ Preset Configurations - Ready-to-use configs for different strictness levels
- π¨ Theme-aware - Encourages consistent use of design tokens
- β‘ Performance - Optimized for large codebases
- ESLint 9.0 or higher
- Node.js 18.0 or higher
- Flat config format (eslint.config.js)
# pnpm
pnpm add -D @poupe/eslint-plugin-tailwindcss @eslint/css
# npm
npm install -D @poupe/eslint-plugin-tailwindcss @eslint/css
# yarn
yarn add -D @poupe/eslint-plugin-tailwindcss @eslint/cssNote: This plugin requires @eslint/css as a peer dependency for CSS parsing.
Create an eslint.config.mjs file with TypeScript type checking:
// @ts-check
import tailwindcss from '@poupe/eslint-plugin-tailwindcss';
export default [
{
files: ['**/*.css'],
language: 'css/css', // Required
plugins: {
tailwindcss,
},
rules: {
'tailwindcss/valid-theme-function': 'error',
'tailwindcss/valid-modifier-syntax': 'error',
'tailwindcss/valid-apply-directive': 'error',
'tailwindcss/no-arbitrary-value-overuse': 'warn',
'tailwindcss/prefer-theme-tokens': 'warn',
'tailwindcss/no-conflicting-utilities': 'error',
},
},
];Use preset configurations for a quick start:
// @ts-check
import tailwindcss from '@poupe/eslint-plugin-tailwindcss';
export default [
{
files: ['**/*.css'],
language: 'css/css',
plugins: {
tailwindcss,
},
rules: {
// Use recommended settings
...tailwindcss.configs.recommended.rules,
// Override specific rules if needed
'tailwindcss/no-arbitrary-value-overuse': 'error',
},
},
];Basic syntax validation only. Good for existing projects.
- β
valid-theme-function - β
valid-modifier-syntax - β
valid-apply-directive - β
no-invalid-at-rules - β
no-invalid-properties - β
no-duplicate-imports - β
no-empty-blocks - β
no-important
Balanced set of rules for most projects.
- β
All rules from
minimal - β
no-conflicting-utilities β οΈ no-arbitrary-value-overuse(warning)β οΈ prefer-theme-tokens(warning)
All rules enabled with strict settings. Best for new projects.
- β All rules as errors
- β Strictest configuration options
- β Includes optional @eslint/css rules (logical properties, relative units, layers)
Here's a comprehensive eslint.config.mjs with TypeScript support:
// @ts-check
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import css from '@eslint/css';
import tailwindcss from '@poupe/eslint-plugin-tailwindcss';
export default [
// JavaScript/TypeScript files
{
files: ['**/*.{js,mjs,cjs,ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
},
plugins: {
'@typescript-eslint': typescript,
},
rules: {
...js.configs.recommended.rules,
...typescript.configs.recommended.rules,
},
},
// CSS files with Tailwind CSS support
{
files: ['**/*.css'],
language: 'css/css',
plugins: {
css,
tailwindcss,
},
rules: {
// @eslint/css rules
'css/no-duplicate-imports': 'error',
'css/no-empty-blocks': 'error',
// Tailwind CSS specific rules
...tailwindcss.configs.recommended.rules,
},
},
];Rules that catch potential bugs or invalid syntax.
| Rule | Description | π§ |
|---|---|---|
| no-conflicting-utilities | Detects conflicting Tailwind utilities that affect the same CSS properties | |
| no-duplicate-imports | Disallow duplicate @import rules | |
| no-empty-blocks | Disallow empty rule blocks and at-rule blocks | |
| no-invalid-at-rules | Disallow invalid at-rule names and syntax | |
| no-invalid-named-grid-areas | Disallow invalid named grid areas in CSS Grid templates | |
| no-invalid-properties | Disallow invalid CSS property names | |
| use-baseline | Enforce use of widely-supported CSS features | |
| valid-apply-directive | Validates the @apply directive usage |
|
| valid-modifier-syntax | Ensures Tailwind modifiers follow correct syntax patterns | |
| valid-theme-function | Validates usage of the theme() function in CSS files |
π§ |
Rules that guide towards better code patterns and maintainability.
| Rule | Description | π§ |
|---|---|---|
| no-arbitrary-value-overuse | Warns when too many arbitrary values are used instead of theme tokens | |
| no-important | Discourage use of !important | |
| prefer-logical-properties | Enforce the use of logical properties over physical properties | π§ |
| prefer-theme-tokens | Suggests using theme tokens instead of hard-coded values | |
| relative-font-units | Prefer relative units (rem/em) over absolute (px) for fonts | |
| use-layers | Encourage use of @layer for better CSS architecture |
Rules that enforce code style and formatting conventions.
| Rule | Description | π§ |
|---|---|---|
| consistent-spacing | Enforces consistent spacing around colons in CSS declarations | π§ |
π§ = Automatically fixable
Rules that catch general CSS syntax errors and invalid constructs.
| Rule | Description | π§ | Status |
|---|---|---|---|
| no-unknown-pseudo-class | Detect invalid pseudo-classes | Planned | |
| no-unknown-pseudo-element | Detect invalid pseudo-elements | Planned |
Rules that enforce modern CSS patterns.
| Rule | Description | π§ | Status |
|---|
Rules specific to Tailwind CSS version management and migration.
| Rule | Description | π§ | Status |
|---|---|---|---|
| version-compatibility | Enforce compatibility with specific Tailwind CSS versions | Planned | |
| deprecated-features | Warn about deprecated Tailwind features | Planned | |
| migrate-imports | Convert Tailwind v3 imports to v4 syntax | π§ | Priority |
| migrate-directives | Update deprecated Tailwind directives | π§ | Priority |
| migrate-config-to-css | Guide migration from JS config to CSS @theme | Planned | |
| migrate-arbitrary-values | Update arbitrary value syntax between versions | π§ | Planned |
Rules that enforce documentation and maintainability.
| Rule | Description | π§ | Status |
|---|---|---|---|
| comment-word-disallowed-list | Disallow specified words in comments | Considering | |
| require-description-comments | Require explanatory comments for complex selectors | Considering | |
| tailwind-comment-directives | Validate Tailwind-specific comment directives | π§ | Considering |
General CSS formatting rules.
| Rule | Description | π§ | Status |
|---|---|---|---|
| indent | Enforce consistent indentation | π§ | Planned |
| brace-style | Enforce consistent brace placement | π§ | Planned |
| block-spacing | Enforce consistent spacing inside blocks | π§ | Planned |
| declaration-block-newline | Enforce line breaks within declaration blocks | π§ | Planned |
| rule-empty-line-before | Require or disallow empty lines before rules | π§ | Planned |
| property-sort-order | Enforce consistent property declaration order | π§ | Planned |
| at-rule-formatting | Format at-rules consistently | π§ | Planned |
| no-unnecessary-whitespace | Disallow unnecessary whitespace | π§ | Planned |
| property-formatting | Format property declarations consistently | π§ | Planned |
| selector-formatting | Format selectors consistently | π§ | Planned |
| value-formatting | Format property values consistently | π§ | Planned |
| media-query-formatting | Format media queries consistently | π§ | Planned |
Rules for Tailwind CSS v4 specific constructs.
| Rule | Description | π§ | Status |
|---|---|---|---|
| at-apply-formatting | Format @apply directives consistently | π§ | Planned |
| theme-formatting | Format @theme blocks consistently | π§ | Planned |
| enforce-class-order | Enforce consistent Tailwind utility class ordering | π§ | Priority |
Rules for consistent comment styles.
| Rule | Description | π§ | Status |
|---|---|---|---|
| comment-formatting | Format comments consistently | π§ | Considering |
| comment-style | Enforce consistent comment syntax | π§ | Considering |
| comment-empty-line-before | Require or disallow empty lines before comments | π§ | Considering |
| comment-capitalization | Enforce consistent comment capitalization | π§ | Considering |
| comment-length | Enforce maximum comment line length | π§ | Considering |
Status Legend:
- Priority: High priority, will be implemented next
- Planned: Scheduled for implementation
- Considering: Under consideration, may be implemented
This plugin extends @eslint/css with Tailwind CSS v4 syntax support:
- β
Directives:
@theme,@import,@plugin,@utility,@variant,@source - β
Functions:
theme(),screen() - β
Arbitrary Values:
[value]syntax - β
Modifiers:
hover:,focus:,sm:,lg:,inert:,target:,open:,starting:,popover-open:,not-*:,in-*:,[&:state]: - β
Stacked Variants:
dark:hover:text-white - β
Custom Variants: Created with
@custom-variant
Full TypeScript support with exported types:
import type { TailwindCSSRules, TailwindCSSConfigs } from '@poupe/eslint-plugin-tailwindcss';// eslint.config.mjs
import tailwindcss from '@poupe/eslint-plugin-tailwindcss';
import typescript from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
export default [
// TypeScript configuration
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'@typescript-eslint': typescript,
},
rules: {
...typescript.configs['recommended-type-checked'].rules,
},
},
// Tailwind CSS configuration
{
files: ['**/*.css'],
...tailwindcss.configs.recommended,
},
];// eslint.config.mjs
import tailwindcss from '@poupe/eslint-plugin-tailwindcss';
import vue from 'eslint-plugin-vue';
export default [
...vue.configs['flat/recommended'],
{
files: ['**/*.{css,vue}'],
...tailwindcss.configs.recommended,
},
];// @ts-check
// eslint.config.mjs
import tailwindcss from '@poupe/eslint-plugin-tailwindcss';
export default [
{
files: ['**/*.{css,pcss,postcss}'],
...tailwindcss.configs.strict,
},
];Make sure you're using ESLint flat config format (ESLint 9+):
// @ts-check
// β
Correct - eslint.config.mjs (ESM format)
export default [
tailwindcss.configs.recommended,
];
// β Wrong - .eslintrc.js (legacy format)
module.exports = {
extends: ['@poupe/eslint-plugin-tailwindcss'],
};Ensure your config targets CSS files:
export default [
{
files: ['**/*.css'], // Don't forget this!
...tailwindcss.configs.recommended,
},
];Contributions are welcome! Please read our contributing guidelines first.
# Install dependencies
pnpm install
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test:coverage
# Build
pnpm build
# Lint and auto-fix
pnpm lint
# Type checking
pnpm type-check
# Run all pre-commit checks
pnpm precommitTests are written using Vitest and ESLint's RuleTester. Each rule should have comprehensive test coverage including:
- Valid code examples
- Invalid code examples with expected errors
- Auto-fix scenarios (where applicable)
- Edge cases and CSS parsing quirks
Example test structure:
import { RuleTester } from 'eslint';
import css from '@eslint/css';
import { ruleName } from '../../rules/rule-name';
const ruleTester = new RuleTester({
language: 'css/css',
plugins: { css },
});
describe('rule-name', () => {
ruleTester.run('tailwindcss/rule-name', ruleName, {
valid: [
// Valid test cases
],
invalid: [
// Invalid test cases with expected errors
],
});
});MIT Β© Apptly Software Ltd