Tailwind CSS: My First Month
I hated Tailwind for the first two weeks. By week three, I couldn't imagine going back. Here's what changed.
#Tailwind #CSS #Styling
The Initial Reaction
My first reaction to Tailwind was the reaction most developers have when they first see it: horror at the idea of writing utility classes directly in HTML. The className strings were long, ugly, and looked like they'd be impossible to maintain. I'd spent years writing semantic CSS — classes like .card and .button-primary that described what an element was rather than how it looked — and Tailwind felt like a rejection of everything I'd learned about good CSS architecture. I spent two weeks quietly convinced that Tailwind was a fad and that anyone using it would regret it within a year.
The thing that changed my mind was building a real feature with it. I needed to build a form with a few input fields, a submit button, and some validation feedback, and I decided to use Tailwind as an experiment. What I found was that I built the form in about half the time it would have taken me with semantic CSS, because I never had to switch between HTML and CSS files, never had to invent class names for elements that didn't need them, and never had to worry about whether a class I was adding would conflict with an existing class elsewhere in the stylesheet. The utility classes were ugly, but they were fast, and the speed came from eliminating the context-switching that semantic CSS requires.
What Changed
By week three, I noticed that I was thinking about styling differently. With semantic CSS, I'd think "what should this element be called?" and then write CSS for that class. With Tailwind, I'd think "what should this element look like?" and write the classes directly. That shift sounds trivial, but it eliminated an entire category of decision fatigue — I no longer spent time inventing class names for layout containers that didn't need semantic identity. A div that exists purely to add padding doesn't need a class name; it needs padding, and Tailwind lets me express that directly without naming it.
The other thing that changed was my relationship with the CSS file. In semantic CSS, the stylesheet grows over time as new components are added, and old classes accumulate because nobody is sure whether they're still used. Tailwind's JIT compiler scans your source files for class names and only includes the CSS for classes you're actually using, which means the stylesheet is always exactly as large as it needs to be, and there's no dead CSS to clean up. The first time I deleted a component and saw the bundle size drop automatically because the classes were no longer being scanned, I was sold. The maintenance burden of a large CSS codebase is real, and Tailwind eliminates it entirely.
The Trade-offs
Tailwind isn't without trade-offs, and being honest about them is part of using it well. The long className strings are genuinely harder to read than semantic classes, and they make the HTML more verbose. The solution is to extract repeated patterns into components — if you're writing the same set of utility classes on every button, extract a Button component that encapsulates those classes. That's good practice anyway, and Tailwind makes it more natural because the styling lives right next to the markup it styles. The other trade-off is that Tailwind's design tokens (colors, spacing, typography) are baked into the utility classes, so changing the design system requires changing the Tailwind config rather than the CSS, which is a different workflow that takes some getting used to.
The dynamic class name gotcha — where Tailwind purges classes that are constructed at runtime rather than written as literal strings — is the most common source of bugs for new Tailwind users. The fix is to use explicit maps rather than string interpolation, which I cover in a later entry. Once you know about it, it's easy to avoid, but it's the kind of thing that bites you once and then never again, and it's worth knowing about before you ship your first Tailwind project to production.