Skip to main content
Utility CSS blocks illustration
#Web#Web development

Utility-First CSS Changed How We Think About Design Systems

We resisted Tailwind CSS for two years. Then we tried it on a real project and understood what the hype was about. Here is our honest take on utility-first CSS after shipping dozens of sites with it.

When Tailwind CSS first appeared, we dismissed it. Classes like text-lg font-bold mt-4 looked like inline styles with extra steps. We were wrong.

After using Tailwind on over twenty production projects, our position has reversed entirely. Utility-first CSS is not a shortcut — it is a fundamentally different approach to styling that solves real problems.

The problem with traditional CSS

In traditional CSS, you write semantic class names and attach styles to them. The theory is clean. The practice is messy.

On any sufficiently large project, you end up with dead CSS that no one dares to remove, specificity conflicts that require increasingly aggressive selectors, and naming debates that consume more time than actual styling.

The cascade, which is supposed to be CSS’s greatest strength, becomes a source of anxiety. Every change might break something somewhere else.

What utility-first actually solves

Utility-first CSS eliminates the naming problem entirely. You do not need to decide whether a component should be called card-header or card-title or hero-heading. You apply the styles directly and move on.

More importantly, utility classes are scoped by nature. Changing the padding on one element cannot possibly affect the padding on another. This makes refactoring fearless.

The other benefit is consistency. When you use a spacing scale of 4, 8, 12, 16, 20, 24 instead of arbitrary pixel values, the design naturally stays on grid. Design tokens become the only options, which means pixel-perfect consistency without manual enforcement.

Tailwind specifically

Tailwind goes beyond generic utility classes by providing a configurable design system. The color palette, spacing scale, typography, breakpoints, and animations are all defined in a single configuration file. Every utility class references this shared system.

The just-in-time compiler generates only the classes you actually use, which means the production CSS is typically smaller than a hand-written stylesheet. On our last audit, a full marketing site with a dozen page templates shipped 12KB of CSS.

The readability objection

The most common criticism is that Tailwind templates are hard to read. We understand the objection. A div with fifteen utility classes is visually dense.

Our answer is component extraction. When a pattern repeats — a card, a button, a navigation link — we extract it into a reusable component. The utility classes live inside the component, and the component interface is clean.

In Astro, this works beautifully. Each component file contains its markup and styles together. There is no context switching between HTML and CSS files.

When Tailwind is the wrong choice

Tailwind is less suitable for applications where content authors need to apply styles through a WYSIWYG editor. It also adds friction if your team has deep CSS expertise and a well-maintained stylesheet that is working well.

We do not recommend switching to Tailwind for its own sake. We recommend it when starting a new project or when existing CSS has become a maintenance burden.

Our workflow

We design in Figma using a token system that maps directly to Tailwind configuration. Spacing, colors, and typography are defined once in Figma and once in the Tailwind config. The translation from design to code is nearly mechanical, which eliminates an entire category of implementation bugs.