When I first encountered Tailwind CSS a few years ago, my immediate reaction was skepticism:
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Click me
</button>
Coming from traditional CSS practices—separation of concerns, semantic class names, BEM—this looked like a regression back to inline styles. I couldn’t see why anyone would want dozens of utility classes cluttering their HTML templates.
At the time, component libraries were also clunky: many interactive widgets required heavy framework runtimes, and dark mode support felt like an afterthought. I wrote off Tailwind as “not for me” and stuck with CSS modules and custom stylesheets.
What Changed: The Maintenance Reality
What eventually forced me to reconsider wasn’t hype, but the maintenance overhead of custom CSS in growing side projects.
In my previous setup with CSS modules:
- Adjusting spacing or font weights meant jumping back and forth between template and stylesheet.
- Dark mode required maintaining custom properties across separate files, making it easy to miss edge cases.
- Minor refactors often risked breaking unrelated UI components due to subtle cascade issues.
When Tailwind released version 4 and improved its vanilla JavaScript and dark mode ergonomics, I decided to test it properly on a non-trivial project.
Why It Clicked
1. Consistent Design Constraints
The biggest misunderstanding I had was thinking Tailwind was just “shorthand CSS in your HTML”.
In practice, Tailwind is a constrained design system. When writing space-y-4 or text-gray-600 dark:text-gray-300, you’re choosing from a predefined, cohesive palette and spacing scale. You don’t have to invent arbitrary pixel values or wonder whether a margin should be 14px or 16px.
2. Zero Cascade Side Effects
With utility classes, every style is scoped directly to the element. Changing the layout of a card in one view has zero chance of breaking a card in another view.
If a particular utility cluster repeats frequently, modern CSS allows extracting it into standard @utility or @layer components rules:
@import "tailwindcss";
@layer components {
.btn-primary {
border-radius: calc(infinity * 1px);
background-color: var(--color-violet-500);
padding-inline: --spacing(5);
padding-block: --spacing(2);
font-weight: var(--font-weight-semibold);
color: var(--color-white);
box-shadow: var(--shadow-md);
&:hover {
@media (hover: hover) {
background-color: var(--color-violet-700);
}
}
}
}
3. Painless Dark Mode
Implementing dark mode across an entire application used to take days of refactoring custom properties. With Tailwind’s dark: modifier, dark mode variants are co-located right next to light mode definitions. Adding dark mode to a full project became a matter of hours.
Summary
Tailwind isn’t necessarily the solution for every scenario—complex graphics, bespoke animations, and highly dynamic layout engines still demand raw CSS.
However, for day-to-day web application development (forms, dashboards, navigation, cards), Tailwind eliminates the mental overhead of naming CSS classes and debugging specificity battles. It allows me to spend less time architecting stylesheets and more time shipping features.