Why Most Devs Misuse Tailwind (And How to Fix It)
The Tailwind Hype Is Real
Tailwind CSS has fundamentally changed how we think about styling in 2026.
It has won the “CSS Wars” for a simple reason: it solves the specificity and
naming problems that have plagued CSS developers since the mid-90s. No more
debating whether a class should be .card-title-inner-wrapper or
.c-card__header. No more worrying if changing a margin in one file will break
a layout in three others.
However, its popularity has come at a cost. Tailwind is deceptively easy to start using, but incredibly easy to misuse. Most developers are treating it like “inline styles on steroids” rather than the constrained design system it was meant to be.

Why Most Devs Misuse Tailwind (And How to Fix It).
The Class Explosion: HTML as a Graveyard
The most common criticism of Tailwind is the “class soup.” We’ve all seen (or written) code that looks like this:
<!-- The "Everything and the Kitchen Sink" approach -->
<div
class="flex flex-col items-center justify-center p-4 m-2 bg-white rounded-lg shadow-md hover:shadow-lg transition-all duration-300 border border-gray-200 w-full max-w-md mx-auto sm:p-6 md:p-8 lg:w-1/2 xl:w-1/3 dark:bg-slate-800 dark:border-slate-700 ring-1 ring-black/5"
>
<!-- Content... -->
</div>
When your HTML structure is buried under a mountain of utility classes, you’ve lost the ability to scan your code effectively. This isn’t just an aesthetic issue; it’s a maintainability nightmare. If you need to change the “primary card style” across 50 pages, and you’ve manually typed these classes every time, you are in for a world of pain.
The @apply Directive: A Seductive Trap
When developers realize their HTML is getting messy, they often reach for the
@apply directive in their CSS files:
/* The "Lazy Refactor" */
.btn-primary {
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-600 transition-colors;
}
This is often a mistake. By leaning heavily on @apply, you are creating a
“Tailwind-flavored” version of traditional CSS. You lose the ability to see
exactly what styles are applied by looking at the HTML, and you reintroduce the
naming and specificity problems Tailwind was supposed to solve.
If you’re using @apply for everything, you’ve just reinvented BEM with a
more verbose syntax.
The Solution: Component-Driven Extraction
The correct way to handle class bloat isn’t to hide it in CSS files, but to extract it into components. Whether you’re using React, Vue, Svelte, or even Hugo partials, the logic should be:
- Write utilities until the pattern repeats.
- Identify the abstraction (e.g., “PrimaryButton,” “UserCard”).
- Extract to a component where those utilities live in exactly one place.
<!-- Much better: Clear intent, encapsulated styles -->
<Card variant="primary" size="lg">
<UserBio name="Val Paliy" role="Senior Developer" />
</Card>
Arbitrary Value Abuse: The Design System Killer
Tailwind is powerful because it’s opinionated. It forces you to pick
from a scale (e.g., p-4, m-8). However, the “Just-in-Time” (JIT)
engine introduced arbitrary values like w-[373px] or text-[#f2f2f2].
I see developers using these everywhere. This is a failure to define a
design system. If you need a specific color or width more than once, it
belongs in your tailwind.config.js.
Pro Tip: The “Clean Config” Rule
Your tailwind.config.js should be the “Single Source of Truth” for
your brand. If you find yourself using [...] syntax more than once for the
same value, it’s time to extend your theme.
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
"brand-primary": "#0055f4", // No more arbitrary hex codes!
},
spacing: {
"safe-area": "env(safe-area-inset-bottom)",
},
borderRadius: {
"xl-plus": "1.25rem",
},
},
},
};
Responsive Design Gone Wrong
Tailwind’s mobile-first approach is brilliant, but it’s often ignored. Many developers write their “desktop” styles first and then try to “fix” them for mobile using prefixes.
The Golden Rule: Styles with no prefix should always be your mobile styles.
Use md:, lg:, etc., only to increase complexity as screen real estate
allows.
| Strategy | Approach | Maintenance Level |
|---|---|---|
| Mobile-First | Base classes = Mobile, Prefixes = Desktop | Low (Predictable) |
| Desktop-First | Base classes = Desktop, Prefixes = Mobile | High (Overriding chaos) |
| Ad-Hoc | Random breakpoints everywhere | Extreme (Technical Debt) |
The Tailwind Cleanliness Test
Before you commit your next PR, run through this checklist:
- Have I used an arbitrary value (
[...]) for something that should be in the theme? - Is my HTML structure still readable, or have I extracted repeated patterns into components?
- Am I using
@applyas a shortcut to avoid thinking about component architecture? - Are my responsive prefixes following a consistent mobile-first strategy?
- Have I purged unused styles (though JIT does this, ensure your content paths are correct)?
The Philosophy of “Utility-First”
To truly “get” Tailwind, you have to embrace the philosophy. It’s not about saving keystrokes; it’s about localizing the impact of changes. When I change a class on a button, I know for a fact that it only affects that button.
When you misuse Tailwind, you’re fighting that localization. You’re
creating global abstractions (@apply) or inconsistent ad-hoc values that
make the system brittle.
Conclusion
Tailwind CSS is an incredible tool when used with discipline. It allows you to build beautiful, consistent UIs at breakneck speed. But like any powerful tool, it requires an understanding of its underlying principles.
Stop treating your HTML like a dump for every utility class you can think of. Configure your theme, embrace component-driven development, and use the platform’s constraints to your advantage.
If you want to see how I apply these principles in a real-world project, check out my Building a Personal Portfolio with Tailwind CSS guide, where I walk through building a performant site without the common pitfalls.

