Better classNames. Less eye-bleed. DX that actually feels good.
tailwind-dx
is a tiny utility that helps you write readable, maintainable Tailwind classNames by organizing them by intent β like layout, spacing, typography, and more.
No more 200-character className
strings that look like a CSS ransom note.
Tailwind is awesome. But reading your class strings after 3 espressos and 5 months of tech debt?
'flex items-center justify-between px-4 py-2 text-gray-800 shadow rounded-md';
Now compare:
twDX({
layout: 'flex items-center justify-between',
spacing: 'px-4 py-2',
typography: 'text-gray-800',
effects: 'shadow',
borders: 'rounded-md',
});
Boom. Readable. Maintainable. Your eyes and your team will thank you.
- π§ Group Tailwind classes by purpose (layout, spacing, typography, etc.)
- π§± Support for reusable class
presets
(likecard
,buttonPrimary
, etc.) - π§Ό Cleaner
className
logic in React, Preact, Solid, whatever. - π¦Ύ Written in TypeScript. Minimal. Zero deps.
- π― ESLint validation for proper class categorization
- π Support for responsive, state, and dark mode variants
- π€ LLM-friendly structure for better AI assistance
tailwind-dx
is designed to be extremely friendly for both LLM coding agents and IDE autocompletion:
- Structured Input: The layer-based organization makes it easy for LLMs to understand and generate code
- ESLint Validation: Automatic validation of class placement in correct layers
- Predictable Patterns: Consistent structure makes it easier for AI to generate correct code
- Self-Documenting: The layer names serve as natural documentation
- IDE Support: Get autocompletion for:
- Layer names (layout, spacing, typography, etc.)
- Class names within each layer
- Variants (responsive, state, dark mode)
Example of LLM-friendly code generation:
// Easy for LLMs to understand and generate
twDX({
layout: 'flex items-center justify-between',
spacing: 'px-4 py-2',
typography: 'text-gray-800 dark:text-white',
effects: 'shadow hover:shadow-lg',
borders: 'rounded-md focus:ring-2',
});
npm install tailwind-dx
or
yarn add tailwind-dx
import { twDX } from 'tailwind-dx';
const className = twDX({
layout: 'flex items-center justify-between',
spacing: 'px-4 py-2',
typography: 'text-gray-800',
borders: 'rounded-md',
effects: 'shadow-md',
});
To enforce proper class placement in your project, add the ESLint plugin:
- Install the plugin:
npm install -D tailwind-dx
- Add to your ESLint config:
For traditional config (.eslintrc.js
):
module.exports = {
plugins: ['tailwind-dx'],
extends: ['plugin:tailwind-dx/recommended'],
// ... your other config
};
For flat config (eslint.config.js
):
import tailwindDxPlugin from 'tailwind-dx/eslint-rules';
export default [
{
plugins: {
'tailwind-dx': tailwindDxPlugin,
},
rules: {
'tailwind-dx/layers': 'error',
},
},
];
Now ESLint will validate that your classes are in the correct layers:
// β This will show an error
twDX({
layout: 'text-lg', // Error: Class "text-lg" should be in the "typography" layer
typography: 'flex', // Error: Class "flex" should be in the "layout" layer
});
// β
This is correct
twDX({
layout: 'flex',
typography: 'text-lg',
});
twDX({
// Responsive variants
layout: 'flex md:flex-col lg:flex-row',
// Dark mode variant
typography: 'text-gray-800 dark:text-white',
// State variants
effects: 'shadow hover:shadow-lg',
borders: 'rounded-md focus:ring-2',
});
twDX({ preset: 'card' });
You can define your own presets in src/index.ts
or extend it in your own wrapper.
const presets = {
card: 'bg-white rounded-lg shadow-md p-4',
buttonPrimary: 'bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700',
centerFlex: 'flex justify-center items-center',
};
export function Card() {
return (
<div
className={twDX({
layout: 'flex flex-col gap-4',
typography: 'text-gray-800 dark:text-white',
spacing: 'p-6',
borders: 'rounded-xl',
effects: 'shadow-lg hover:shadow-xl',
})}
>
<h2 className={twDX({ typography: 'text-xl font-bold' })}>
Hello, TailwindDX
</h2>
<p
className={twDX({
typography: 'text-sm text-gray-500 dark:text-gray-400',
})}
>
Clean classNames, clean mind.
</p>
</div>
);
}
Want conditional classes? Use with clsx
or classnames
:
import { clsx } from 'clsx';
const className = clsx(
twDX({ preset: 'card' }),
isActive && 'ring-2 ring-blue-500',
);
- π§© Plugin system for presets
- π§ VSCode IntelliSense extension
- π§Ό Linter plugin to auto-split long classNames into twDX
- πͺ Babel/TS transform that turns
twDX
into strings at build time
Got ideas? Found bugs? Want to add a tailwind-dx
theme song? Open a PR or issue. We're just getting started.
MIT β use it, abuse it, remix it.
Writing Tailwind should feel like composing UI, not like decoding The Matrix.
Let tailwind-dx
clean up your className
soup. π