eslint-plugin-tailwind-grouping
v1.1.0
Published
ESLint plugin to group and organize Tailwind CSS classes
Maintainers
Readme
ESLint Plugin: Tailwind Grouping
An ESLint plugin that automatically groups and organizes Tailwind CSS classes into semantic categories, improving readability and maintainability of your React/JSX code.
📋 Table of Contents
Problem
Large Tailwind className strings are difficult to read and maintain:
<select
className="border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed"/>Solution
The plugin automatically transforms long className strings into organized, commented groups using clsx:
<select
className={clsx(
// Size
"h-9 w-full min-w-0",
// Spacing
"px-3 py-2 pr-9",
// Border
"border border-input outline-none rounded-md",
// Background
"bg-transparent dark:bg-input/30 dark:hover:bg-input/50 selection:bg-primary",
// Text
"text-sm selection:text-primary-foreground placeholder:text-muted-foreground",
// Effects
"appearance-none shadow-xs transition-[color,box-shadow]",
// Others
"disabled:pointer-events-none disabled:cursor-not-allowed"
)}
/>Installation
npm install --save-dev eslint-plugin-tailwind-grouping
# or
yarn add -D eslint-plugin-tailwind-grouping
# or
pnpm add -D eslint-plugin-tailwind-groupingUsage
ESLint Configuration (Flat Config - ESLint 9+)
// eslint.config.js
import tailwindGrouping from 'eslint-plugin-tailwind-grouping';
export default [
{
plugins: {
'tailwind-grouping': tailwindGrouping
},
rules: {
'tailwind-grouping/group-classes': ['warn', {
threshold: 5,
include: ['**/*.tsx', '**/*.jsx'],
exclude: ['**/*.test.tsx']
}]
}
}
];ESLint Configuration (Legacy)
{
"plugins": [
"tailwind-grouping"
],
"rules": {
"tailwind-grouping/group-classes": [
"warn",
{
"threshold": 5
}
]
}
}Using with Prettier
This plugin is compatible with Prettier. Make sure to run ESLint with --fix flag:
eslint --fix .Configuration
Options
{
// Minimum number of classes required to trigger transformation
// Default: 0
threshold?: number;
// Glob patterns for files to include
// Default: [] (all files)
include?: string[];
// Glob patterns for files to exclude
// Default: [] (no exclusions)
exclude?: string[];
// Custom mapping of groups to class patterns
// Default: DEFAULT_GROUP_MAPPING
mapping?: {
[groupName: string]: string[];
};
// Custom order of groups
// Default: ['Size', 'Layout', 'Spacing', 'Border', 'Background', 'Text', 'Effects', 'Others']
groupOrder?: string[];
// Name of the utility function to use
// Default: 'clsx'
utilityFunction?: string;
// Whether to include group name comments in the output
// Default: true
showGroupNames?: boolean;
// Comment template for group names
// Can be a preset name or a custom template string with variables
// Default: "// {groupName}"
// Presets: 'line', 'block', 'jsdoc', 'bracket', 'numbered', 'verbose'
// Variables: {groupName}, {index}, {count}
commentTemplate?: string;
// Sorting order for classes within each group
// Default: "no-sort"
// Options: 'no-sort' | 'asc' | 'desc' | 'official'
order?: 'no-sort' | 'asc' | 'desc' | 'official';
}Example: Custom Mapping
{
"tailwind-grouping/group-classes":
["warn", {
"threshold": 3,
"mapping": {
"Size": ["w-*", "h-*", "min-w-*", "max-w-*"],
"Colors": ["bg-*", "text-*", "border-*"],
"Spacing": ["p-*", "m-*", "gap-*"],
"Others": []
},
"groupOrder": ["Size", "Colors", "Spacing", "Others"]
}]
}Example: Custom Utility Function
{
"tailwind-grouping/group-classes":
["warn", {
"utilityFunction": "cn" // Use shadcn/ui's cn function
}]
}Example: Without Group Name Comments
If you prefer cleaner output without the group name comments, you can disable them:
{
"tailwind-grouping/group-classes":
["warn", {
"showGroupNames": false
}]
}Output with showGroupNames: false:
<div
className={clsx(
"h-9 w-full",
"px-3 py-2",
"border rounded-md",
"bg-white"
)}
>
Content
</div>Output with showGroupNames: true (default):
<div
className={clsx(
// Size
"h-9 w-full",
// Spacing
"px-3 py-2",
// Border
"border rounded-md",
// Background
"bg-white"
)}
>
Content
</div>Example: Custom Comment Templates
Customize how group comments appear using templates with variables or presets:
Using Preset Templates
{
"tailwind-grouping/group-classes": ["warn", {
"commentTemplate": "block" // Use block comment style
}]
}Available Presets:
line:// {groupName}(default)block:/* {groupName} */jsdoc:/** {groupName} **/bracket:// [{groupName}]numbered:// {index}. {groupName}verbose:// {groupName} ({count} classes)
Using Custom Templates
Create your own templates with these variables:
{groupName}: Name of the group (e.g., "Size", "Spacing"){index}: 1-based position of the group{count}: Number of classes in the group
{
"tailwind-grouping/group-classes": ["warn", {
"commentTemplate": "// {index}. {groupName} ({count})"
}]
}Example outputs:
// With commentTemplate: "/* {groupName} */"
<div
className={clsx(
/* Size */
"h-9 w-full",
/* Spacing */
"px-3 py-2"
)}
/>
// With commentTemplate: "// {index}. {groupName}"
<div
className={clsx(
// 1. Size
"h-9 w-full",
// 2. Spacing
"px-3 py-2"
)}
/>
// With commentTemplate: "// {groupName} ({count})"
<div
className={clsx(
// Size (2)
"h-9 w-full",
// Spacing (2)
"px-3 py-2"
)}
/>
// With commentTemplate: "// [{index}] {groupName} - {count} classes"
<div
className={clsx(
// [1] Size - 2 classes
"h-9 w-full",
// [2] Spacing - 2 classes
"px-3 py-2"
)}
/>Example: Class Sorting
You can control how classes are sorted within each group:
{
"tailwind-grouping/group-classes": ["warn", {
"order": "asc" // Sort alphabetically A-Z
}]
}Options:
"no-sort"(default): Preserve original order"asc": Sort alphabetically A-Z"desc": Sort alphabetically Z-A"official": Use Tailwind's official class ordering (same as prettier-plugin-tailwindcss)
Example output with order: "asc":
<div
className={clsx(
// Size
"h-9 min-w-0 w-full", // Sorted alphabetically
// Spacing
"px-3 py-2"
)}
/>Note: The plugin automatically removes duplicate classes, keeping only the first occurrence.
Examples
Before
<div
className="flex items-center justify-between p-4 bg-white border rounded-lg shadow-md hover:shadow-lg transition-shadow">
Content
</div>After (with threshold: 3)
<div
className={clsx(
// Layout
"flex items-center justify-between",
// Spacing
"p-4",
// Border
"border rounded-lg",
// Background
"bg-white",
// Effects
"shadow-md hover:shadow-lg transition-shadow"
)}
>
Content
</div>Handling Modifiers
The plugin preserves all Tailwind modifiers (responsive, state, dark mode, etc.):
// Input
className = "md:flex lg:grid hover:bg-blue-500 dark:bg-gray-900"
// Output
className = {
clsx(
// Layout
"md:flex lg:grid",
// Background
"hover:bg-blue-500 dark:bg-gray-900"
)
}Handling Arbitrary Values
Arbitrary values are properly grouped:
// Input
className = "w-[500px] h-[calc(100vh-80px)] bg-[#ff0000]"
// Output
className = {
clsx(
// Size
"w-[500px] h-[calc(100vh-80px)]",
// Background
"bg-[#ff0000]"
)
}Development
Setup
# Install dependencies
npm install
# Build the plugin
npm run build
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Lint the code
npm run lintRunning Tests
npm testTesting Locally
To test the plugin in a project before publishing:
# In the plugin directory
npm link
# In your project directory
npm link eslint-plugin-tailwind-groupingDesign Principles
- Immutability: All domain objects are immutable
- Single Responsibility: Each class has one clear purpose
- Dependency Injection: Services receive dependencies via constructor
- Testability: Pure business logic separated from infrastructure
- Type Safety: Full TypeScript coverage with strict mode
Contributing
Contributions are welcome! Please ensure:
- All tests pass (
npm test) - Code follows the existing architecture
- New features include tests
- TypeScript types are properly defined
License
MIT
