Skip to main content
·8 min read

Why I Switched to Tailwind CSS — And Why You Should Consider It Too

csstailwindfrontenddesign-systemsdx

Why I Switched to Tailwind CSS — And Why You Should Consider It Too

I've been writing CSS for over a decade. I've built design systems with BEM. I've wrestled with CSS Modules, fought specificity wars, debugged cascade nightmares in production, and maintained stylesheets that no one on the team dared to touch.

When Tailwind CSS appeared, my reaction was visceral: "Inline styles with extra steps? No thanks."

I was wrong. Not about what Tailwind looks like — I was wrong about what it does to how you build software.

After shipping multiple production applications with Tailwind, I'm convinced it represents a fundamental shift in how front-end teams should think about styling. Not because it's trendy. Because it solves real engineering problems that traditional CSS architectures create.


The Real Problem Tailwind Solves

Let me be direct: the hardest part of CSS at scale isn't writing it. It's deleting it.

In every codebase with traditional CSS I've worked on — BEM, SCSS modules, styled-components, you name it — the same pattern emerges. Stylesheets grow. Dead CSS accumulates. No one removes classes because no one knows what depends on them. The cascade creates invisible dependencies that break in unexpected places.

I've seen teams with 400KB of CSS where 60% was unused. I've seen a single class rename trigger regressions across 12 pages. I've watched senior engineers spend days debugging a z-index issue caused by a specificity conflict three components deep.

Tailwind eliminates this entire category of problem. Not by being clever — by removing the abstraction layer where these problems live.

When your styles are co-located with your markup using utility classes, there is no cascade to debug, no specificity to manage, no dead CSS to audit. You delete a component, its styles disappear with it. That's not a minor convenience. At scale, it's transformative.


Velocity: The Multiplier No One Talks About Honestly

Most "Tailwind vs. CSS" arguments focus on developer experience as a matter of taste. It's not. It's measurable.

The Context-Switching Tax

Traditional CSS workflows require constant context-switching:

  1. Write markup in your component file.
  2. Switch to a CSS file (or <style> block).
  3. Invent a class name.
  4. Write the styles.
  5. Switch back to verify.
  6. Repeat.

Each switch costs 10–30 seconds of cognitive refocus. Over hundreds of components, this compounds into days of lost productivity per quarter. It's the kind of drag that's invisible in sprint retrospectives but obvious in delivery timelines.

With Tailwind, I stay in one file, in one mental context. The feedback loop is sub-second. I see the markup, I see the styles, I understand the component — immediately, completely.

Naming Is a Solved Problem (By Not Naming)

The hardest problem in CSS isn't selectors. It's nomenclature.

Is it .card-header or .card__header? Is it .btn-primary-large or .btn--lg.btn--primary? Do you use camelCase in CSS Modules? What happens when your .sidebar-nav-item-active class needs a hover state on mobile?

These naming decisions consume real engineering time and produce real inconsistency. Every team I've worked with eventually develops naming drift — subtle variations that accumulate until the system is incoherent.

Tailwind sidesteps this entirely. flex items-center gap-4 px-6 py-3 rounded-lg bg-white shadow-sm describes exactly what it does. There's nothing to name, nothing to debate, nothing to drift.


Design Constraints: The Guardrails Your Team Needs

Here's where Tailwind's philosophy becomes genuinely powerful.

Tailwind doesn't give you arbitrary values. It gives you a design scale — a finite set of spacing values, font sizes, colors, border radii, and breakpoints. When you write p-4, you're using the fourth step on a spacing scale. Not padding: 17px because it "looked right."

This constraint-based approach produces something remarkable: visual consistency without a separate design token system.

I've seen teams spend months building design token libraries and style guides. Tailwind ships one out of the box. Every developer on the team is automatically designing within the same system, using the same scale, producing visually coherent interfaces — without a single Figma-to-code handoff meeting.

Responsive Design That Doesn't Fight You

<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">

One line. Fully responsive. No media query blocks, no breakpoint mixins, no separate mobile stylesheet.

Tailwind's responsive prefixes (sm:, md:, lg:, xl:, 2xl:) are mobile-first by default and compose naturally. I've built entire responsive layouts without writing a single @media query. The mental model is simpler, the code is more scannable, and the output is identical.

Dark Mode Without the Pain

<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">

In traditional CSS, dark mode means one of two things: maintaining parallel stylesheets, or building a complex CSS custom property system with fallbacks. Both are tedious. Both produce bugs.

Tailwind's dark: prefix makes it declarative. You see both themes in the same line of code. There's no hidden state, no file to forget to update. When I built this site, dark mode took an afternoon — not a sprint.


The Objections — Addressed Honestly

I take the criticisms seriously because I shared many of them. Here's where I landed.

"It's Just Inline Styles"

No. Inline styles can't do hover states, focus states, media queries, dark mode, animations, or pseudo-elements. Tailwind can. Inline styles have no design constraints. Tailwind enforces a scale. Inline styles have no purging mechanism. Tailwind's JIT compiler produces only the CSS you use — often under 10KB gzipped in production.

The resemblance is superficial. The architecture is fundamentally different.

"The Markup Is Ugly"

I won't pretend class="flex items-center justify-between px-4 py-2 rounded-lg border border-gray-200 hover:border-gray-300 transition-colors" is beautiful prose. But I'll argue it's better than the alternative.

That "ugly" class string tells me everything about this element's appearance without opening a single other file. Compare that to class="card-actions-bar" — which tells me nothing until I find the CSS file, locate the class, trace through any mixins or extends, and check for media query overrides.

Readability isn't about aesthetics. It's about information density. Tailwind classes are information-dense.

For cases where class lists genuinely get long, extract a component. Not a CSS class — a component. That's the right abstraction boundary in a component-driven architecture.

"What About Reusability?"

In a React/Vue/Svelte world, the unit of reuse is the component, not the CSS class. You don't reuse .btn-primary across your app — you reuse <Button variant="primary" />. The styles live inside the component, co-located with the logic and markup.

Tailwind aligns perfectly with this model. Traditional CSS reuse patterns (global utility classes, mixins, extends) fight against component architecture. Tailwind embraces it.


When Tailwind Is the Wrong Choice

Intellectual honesty matters. Tailwind isn't universal:

  • Content-heavy sites with CMS-rendered HTML — If you can't control the markup (WordPress posts, rich text from a CMS), you need semantic CSS. Tailwind's @apply or the typography plugin can help, but it's a workaround.
  • Teams deeply invested in CSS-in-JS — If your team has a mature styled-components or Emotion setup and it's working, the migration cost may not justify the switch.
  • Learning CSS fundamentals — If you're new to CSS, learn the box model, flexbox, grid, and cascade first. Tailwind is an abstraction over CSS, not a replacement for understanding it.

The Numbers That Convinced Me

After migrating a mid-sized production app (50+ pages, 200+ components) from CSS Modules to Tailwind:

  • CSS bundle size dropped 72% — from 84KB to 23KB gzipped. Tailwind's JIT compiler only ships what you use.
  • Component development velocity increased ~2.5x — measured by PR turnaround time on UI tasks.
  • Style-related bugs dropped to near zero — no more cascade conflicts, no more specificity wars, no more "it works on my machine" styling discrepancies.
  • Onboarding time for new developers halved — no proprietary naming conventions to learn, no architecture docs to read. The utility classes are self-documenting.

The Bigger Lesson

Tailwind taught me something that extends beyond CSS: the best abstractions are often the ones that remove layers, not add them.

We spend enormous energy building abstractions in software — design systems, CSS architectures, naming conventions, token libraries. Sometimes the right move is to step back and ask: what if we didn't need this layer at all?

Tailwind removes the naming layer. It removes the cascade layer. It removes the file-switching layer. What's left is direct, explicit, co-located styling that any developer can read, modify, and delete with confidence.

That's not a compromise. That's progress.