Why utility-first CSS frameworks can slow you down, and how daisyUI solves the maintainability crisis of utility classes
Tailwind CSS revolutionized how we think about CSS by introducing utility-first styling. But after years of using it in real projects, many developers are hitting walls that utility classes alone can't solve. Let's explore why pure utility-first approaches create problems, and how you can get the best of both worlds.
Tailwind CSS promised to solve CSS problems, but it introduced new ones. Here's what happens when you rely only on utility classes:
Real-world components quickly turn into utility class soup:
<!-- A simple button becomes this monster -->
<button class="inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed transition duration-150 ease-in-out">
Submit Form
</button>
<!-- And a card component? Good luck -->
<div class="max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden md:max-w-2xl m-3 hover:shadow-xl transition-shadow duration-300">
<div class="md:flex">
<div class="md:shrink-0">
<img class="h-48 w-full object-cover md:h-full md:w-48" src="image.jpg" alt="Photo">
</div>
<div class="p-8">
<div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Article</div>
<a href="#" class="block mt-1 text-lg leading-tight font-medium text-black hover:underline">Title</a>
<p class="mt-2 text-slate-500">Description text goes here...</p>
</div>
</div>
</div>
This is barely readable. Imagine maintaining hundreds of components like this.
When every component is a wall of utility classes, developers resort to copying and pasting:
<!-- Button 1 -->
<button class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
Save
</button>
<!-- Button 2 - copied and modified -->
<button class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
Publish
</button>
<!-- Button 3 - more copying -->
<button class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 focus:ring-2 focus:ring-red-500 focus:ring-offset-2">
Delete
</button>
Need to update the button style? Now you have to find and update dozens of places. This is the exact problem CSS was supposed to solve.
Utility classes repeat everywhere in your HTML. A typical page might look like:
<div class="flex flex-col space-y-4 p-6 bg-white rounded-lg shadow-lg">
<div class="flex items-center space-x-3">
<div class="flex-shrink-0 w-10 h-10 bg-gray-300 rounded-full"></div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">Username</p>
<p class="text-sm text-gray-500 truncate">[email protected]</p>
</div>
</div>
<div class="flex items-center space-x-3">
<div class="flex-shrink-0 w-10 h-10 bg-gray-300 rounded-full"></div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">Another User</p>
<p class="text-sm text-gray-500 truncate">[email protected]</p>
</div>
</div>
</div>
Classes like flex
, items-center
, text-sm
, and text-gray-900
repeat constantly. This bloats your HTML and makes it harder to scan.
Building even simple interfaces requires intimate knowledge of:
justify-between
, items-center
, flex-shrink-0
, truncate
md:flex
, lg:grid-cols-3
, xl:space-x-8
hover:bg-blue-700
, focus:ring-2
, disabled:opacity-50
flex-shrink-0
actually doesYou need to be a CSS expert just to create a basic webpage. New developers get overwhelmed trying to remember hundreds of class names.
Want to change your design system? Good luck:
<!-- Need to change all buttons from blue to green? -->
<!-- Find and replace across hundreds of files -->
<button class="bg-blue-600 hover:bg-blue-700 text-white">Button 1</button>
<button class="bg-blue-600 hover:bg-blue-700 text-white">Button 2</button>
<button class="bg-blue-600 hover:bg-blue-700 text-white">Button 3</button>
<!-- ... and 200 more buttons -->
This is exactly the maintenance problem that CSS classes were invented to solve in the first place.
Different developers write the same styles in different ways:
<!-- Developer A's button -->
<button class="px-4 py-2 bg-blue-600 text-white rounded">Click me</button>
<!-- Developer B's "same" button -->
<button class="py-2 px-4 text-white bg-blue-600 rounded-md">Click me</button>
<!-- Developer C's version -->
<button class="p-2 px-4 bg-blue-600 text-white rounded-sm">Click me</button>
These all look slightly different, creating inconsistent UIs. Without component-level abstractions, maintaining design consistency is nearly impossible.
Tailwind CSS documentation makes it look easy, but real-world usage requires extensive knowledge:
You need to understand:
justify-center
vs items-center
grid-cols-12
and col-span-4
work togetherp-4
and space-y-4
behave differentlytext-lg
, font-semibold
, and leading-6
relative
, absolute
, or sticky
positioningCreating good-looking interfaces requires:
text-sm
vs text-base
space-y-4
relates to p-6
This is advanced knowledge that most developers don't have. The result? Websites that technically work but look amateurish.
daisyUI keeps all the power of Tailwind CSS while fixing the maintainability issues. Here's how:
Instead of utility soup, use meaningful names:
<!-- Instead of this mess -->
<button class="inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Submit
</button>
<!-- Write this -->
<button class="btn btn-primary">Submit</button>
Your HTML is readable again. Anyone can understand what btn btn-primary
means.
No more guessing about colors, spacing, or typography:
<!-- Professionally designed components out of the box -->
<div class="card w-96 bg-base-100 shadow-xl">
<figure><img src="photo.jpg" alt="Album" /></figure>
<div class="card-body">
<h2 class="card-title">Card Title</h2>
<p>Card description goes here.</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Buy Now</button>
</div>
</div>
</div>
Everything looks cohesive because it's designed as a system, not assembled from random utilities.
Need to update your button style? Change it once in your CSS:
/* Update all buttons globally */
.btn {
/* Your custom button styles */
padding: 12px 24px;
border-radius: 8px;
/* etc. */
}
Or override specific variants:
.btn-primary {
background-color: your-brand-color;
}
All your buttons update automatically. No find-and-replace across hundreds of files.
Start with semantic classes and add utilities when needed:
<!-- Start simple -->
<button class="btn btn-primary">Click me</button>
<!-- Add utilities for customization -->
<button class="btn btn-primary lg:btn-lg xl:w-full">Click me</button>
<!-- Or create variations -->
<button class="btn btn-primary hover:scale-105 transition-transform">Click me</button>
You can be productive immediately and learn advanced techniques over time.
Everyone uses the same component classes:
<!-- Every developer writes buttons the same way -->
<button class="btn btn-primary">Primary Action</button>
<button class="btn btn-secondary">Secondary Action</button>
<button class="btn btn-outline">Outline Button</button>
No more style variations or inconsistent spacing. Your UI stays consistent as your team grows.
You still get all of Tailwind's power when you need it:
<!-- Semantic base with utility customization -->
<div class="card lg:card-side bg-base-100 shadow-xl">
<figure class="lg:w-1/3">
<img src="photo.jpg" alt="Album" class="w-full h-full object-cover" />
</figure>
<div class="card-body lg:w-2/3">
<h2 class="card-title text-2xl lg:text-3xl">Card Title</h2>
<p class="text-base-content/70">Card description here.</p>
<div class="card-actions justify-end mt-4">
<button class="btn btn-primary">Action</button>
</div>
</div>
</div>
Semantic structure with utility fine-tuning where needed.
daisyUI also improves performance:
Compare file sizes:
<!-- Pure Tailwind: 2,847 characters -->
<div class="max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden md:max-w-2xl m-3 hover:shadow-xl transition-shadow duration-300">
<div class="md:flex">
<div class="md:shrink-0">
<img class="h-48 w-full object-cover md:h-full md:w-48" src="image.jpg" alt="">
</div>
<div class="p-8">
<div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Article</div>
<a href="#" class="block mt-1 text-lg leading-tight font-medium text-black hover:underline">Title</a>
<p class="mt-2 text-slate-500">Description text...</p>
</div>
</div>
</div>
<!-- daisyUI: 456 characters -->
<div class="card lg:card-side bg-base-100 shadow-xl">
<figure><img src="image.jpg" alt="Album" /></figure>
<div class="card-body">
<div class="badge badge-secondary">Article</div>
<h2 class="card-title">Title</h2>
<p>Description text...</p>
</div>
</div>
That's 84% smaller HTML. Multiply this across your entire site and the savings add up.
Shorter, repeated class names compress better:
<!-- Compresses well -->
<button class="btn">Button 1</button>
<button class="btn">Button 2</button>
<button class="btn">Button 3</button>
<!-- Compresses poorly -->
<button class="px-4 py-2 bg-blue-600 text-white rounded">Button 1</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded">Button 2</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded">Button 3</button>
You don't have to rewrite everything:
<!-- Old utility approach -->
<button class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Old Button
</button>
<!-- New semantic approach -->
<button class="btn btn-primary">New Button</button>
Replace utility combinations with semantic classes over time. Your codebase gets cleaner with each update.
daisyUI works with regular Tailwind utilities:
<div class="card w-96 bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
Card Title
<div class="badge badge-secondary">NEW</div>
</h2>
<p>Card description.</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Buy Now</button>
</div>
</div>
</div>
Teams report significant improvements after switching to daisyUI:
Tailwind CSS is powerful, but pure utility-first approaches create maintainability problems. daisyUI gives you the best of both worlds: semantic components for productivity and utilities for customization.
Get started with daisyUI and experience what CSS-in-HTML should be: readable, maintainable, and fast to write.
Stop fighting with utility classes and start building better UIs faster.
Used by engineers at