The Utility Class Debate Deserves Better Arguments

There’s no shortage of hot takes about CSS utility frameworks. Some love them, some hate them, and a lot of the criticism in between is, frankly, lazy. The most persistent bad take is the one that equates utility classes with inline styles. It keeps coming up despite being demonstrably wrong.

The most tired, overused cliché is that utility classes are just inline styles.

The distinction is clear enough with a concrete example:

<div style="color: #3ea8ca;"></div>

<div class="color-blue"></div>

The first element hardcodes a specific blue value directly in the markup. The second uses a class to apply the same color from an external stylesheet. Over the lifetime of that class, the shade of blue could change without ever touching the HTML. That’s an abstraction, even if a narrow one. The same logic applies to sizing utilities. A class like size-xl might define padding in one place, and updating that class updates every element using it—no markup changes required. An inline style="padding: 10px;" locks you into the markup, and changing it means editing HTML.

To be fair, utility frameworks do include classes that are essentially indistinguishable from inline styles. A Tailwind utility like top-0 is just top: 0. There’s no configuration behind it; the name says it all. Nothing in the stylesheet will ever reassign that class meaningfully. Those are genuinely inline-style-like. But that doesn’t define the whole category.

Where utility frameworks differ from inline styles is in their constraints. Inline styling has no guardrails beyond the browser’s limits—no pseudo-selectors, no media queries, and no consistency baked in. A limited set of utility classes imposes structure. You can only reach for what exists. That usually translates to a more visually coherent interface.

Think of utility frameworks as bumper bowling for styling. You may not roll a perfect game, but you’re not going to embarrass yourself with a gutter ball.

The “Bloat” Argument Misses the Point

Another favorite criticism is that utility frameworks force you to ship excessive CSS. If you do, you’re implementing the approach wrong. The entire value proposition hinges on sending only the classes you actually use. That kind of tree-shaking is easier said than done—there’s real complexity and potential technical debt in the build pipeline required to pull it off reliably. But done right, fewer CSS bytes on the wire is a genuine performance win, and Tailwind in particular pushes you toward that outcome.

There Are Still Legitimate Complaints

None of that makes utility frameworks above criticism. Plenty of fair objections exist.

For one, they’re hard on the eyes. Duplicating a dozen or more classes across every element in a template obscures the structure you’re trying to work with. It’s harder to find the classes doing non-visual work. It feels harder to refactor cleanly. Semantic clarity takes a backseat to presentational intent, and that gets ugly fast.

There’s also a developer experience gap that doesn’t get discussed enough. Utility-first workflows assume modern tooling with Hot Module Reloading, where CSS updates land instantly without refreshing the page. Static sites and SSGs don’t usually have that luxury. Tools like Browsersync handle CSS injection well, but they can’t inject new HTML—they just trigger a full reload. Authoring utility-heavy markup without HMR means spending a lot of time waiting on refreshes, and that’s a genuine drawback worth criticizing.