
If you’ve ever stared at a massive, tangled Tailwind class string and thought “there has to be a better way” — you’re not alone.
<div className="bg-amber-500 text-nowrap hover:bg-slate-600 hover:text-clip md:focus:text-blue-700 md:hover:bg-gray-800 dark:bg-gray-900 dark:hover:bg-gray-700 ..." />
Unreadable. Unmaintainable. Unpleasant to work with.
So I built Tailwind Tree — a way to write Tailwind classes that scales with complexity and matches the way your brain thinks about UI states.
After years of building UIs with Tailwind CSS, I kept running into the same problem:
As components grew, the class strings turned into dense, brittle walls of text.
Variants nested inside variants.
Responsive rules stacked on interactive states.
Dark mode classes hidden inside 200-character strings.
Tailwind Tree solves this by letting you write those classes as a structured tree instead of a chaotic blob.
Here’s a realistic scenario: a button with:
Base styling
Hover & focus states
Responsive sizes
Active & disabled states
Dark mode variants
Typical Tailwind:
<button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 focus:ring-2 focus:ring-blue-300 md:px-6 md:py-3 lg:text-lg active:bg-blue-700 disabled:opacity-50 dark:bg-blue-900 dark:hover:bg-blue-800" />
Problems:
Your eyes bounce around trying to figure out what applies where.
With Tailwind Tree, the same component looks like this:
twTree([ 'px-4 py-2 bg-blue-500 text-white rounded', { hover: 'bg-blue-600', focus: 'ring-2 ring-blue-300', active: 'bg-blue-700', disabled: 'opacity-50', md: 'px-6 py-3 text-lg', dark: 'bg-blue-900' }, { hover: { dark: 'bg-blue-800' } } ])
Readable. Structured. Maintainable.
You can instantly see which states contain which classes.
Tailwind Tree supports complex UI logic:
twTree([ 'flex items-center gap-4', isActive ? 'bg-green-500' : 'bg-gray-300', { hover: [ 'opacity-80', isFocused ? 'shadow-lg' : 'shadow-md', { dark: 'bg-gray-700' } ], focus: { visible: 'ring-2 ring-green-500', dark: 'ring-green-300' }, md: [ 'gap-6', { hover: 'scale-105' } ] } ])
It compiles cleanly to the exact Tailwind classes your browser needs.
Tailwind Tree is version-agnostic.
Use the @tailwind-tree/extractor to parse twTree() and purge unused classes.
Use the @tailwind-tree/vite-plugin to inject safelisted classes during Vite’s build.
Both require minimal configuration.
Classes are grouped by meaning, not crammed together.
Updating a hover, dark, md, or active class is simple — everything is in its logical place.
Even deeply nested states stay clean.
With TypeScript, variant names autocomplete and type errors are surfaced.
Only used classes end up in production.
Installation:
pnpm add tailwind-tree # or npm install tailwind-tree # or yarn add tailwind-tree
Usage:
import { twTree } from 'tailwind-tree'; className={twTree([ 'base classes', { variant: 'classes', nested: { deeper: 'classes' } } ])}
Tailwind is incredible for utility-first styling.
But as UIs grow, managing long class strings becomes a mental tax.
Tailwind Tree bridges that gap by giving you:
Structure
Nesting
Readability
Maintainability
Zero Tailwind friction
It lets you express complex CSS logic without losing clarity.
Tailwind Tree is open source, free forever, and community-driven.
Contributions and feedback are welcome!
If you've ever groaned at a 300-character Tailwind class string, give Tailwind Tree a try.
It might change how you write Tailwind — forever.
Made with 💙 by Shervin Ghajar