Tailwind CSS: Why Utility-First Works (Even Though It Looks Wrong)
A practical guide to Tailwind CSS covering setup, essential classes, responsive design, dark mode, @apply extraction, and why the 'ugly HTML' argument misses the point.
The first time you see Tailwind CSS, you'll probably hate it. A Then you build something real with it, and you don't want to go back. The traditional CSS approach sounds great in theory: write semantic class names, keep styles in separate files, reuse classes across components. In practice, here's what happens: The "ugly HTML" complaint is real but misplaced. You're not reading raw HTML files — you're reading components. A React component called Install Tailwind in a new project: Add it to your Import Tailwind in your main CSS file: That's it. No configuration file needed for basic usage — Tailwind v4 auto-detects your content files. Tailwind uses mobile-first breakpoints as prefixes: Breakpoints: No media queries to write. No separate mobile stylesheet. The responsive behavior lives right next to the base styles, which makes it trivial to understand what a component looks like at each breakpoint. Add By default, Tailwind uses the State variants work like responsive prefixes: Group hover is useful for card-style interactions: Tailwind provides This is tempting. Resist using it for everything. If you extract every repeated pattern into an Tailwind is faster for building UIs. Significantly faster. You stay in one file, you don't context-switch between HTML and CSS, and the constraint system (fixed spacing scale, predefined colors) makes design consistency almost automatic. The trade-off is readability of individual elements. A For small projects, either approach works. For anything that'll be maintained by a team over months or years, Tailwind wins on maintainability. The best way to get comfortable with Tailwind is to build something. Pick a design you like, try to recreate it with utility classes, and notice how fast you move once the class names click. If you want structured practice building real frontend components, CodeUp is a solid place to sharpen those skills interactively.Why Utility-First Actually Works
Tailwind flips this around. Instead of naming your styles, you compose them directly. When you delete a component, its styles go with it. No dead CSS. No naming debates. No specificity wars.
.card-header for the first card component.card-header-alt or .card-header--large is just as semantic as a Getting Started
npm install -D tailwindcss @tailwindcss/postcss postcsspostcss.config.js:module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
};@import "tailwindcss";Essential Classes You'll Use Constantly
Layout:
Spacing:
flex, grid, block, hiddenitems-center, justify-between, gap-4w-full, max-w-xl, h-screen
Typography:
p-4 (padding 1rem all sides), px-6 (horizontal padding), py-2 (vertical)m-auto, mt-8, mb-4
Borders and shadows:
text-sm, text-lg, text-2xlfont-bold, font-mediumtext-gray-700, text-blue-500leading-relaxed, tracking-wide
Backgrounds:
rounded-lg, rounded-fullborder, border-gray-200shadow-md, shadow-lg
You learn these by using them. After a week, the most common ones are muscle memory.
bg-white, bg-gray-50, bg-blue-600bg-gradient-to-r from-blue-500 to-purple-600Responsive Design
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- 1 column on mobile, 2 on tablet, 3 on desktop -->
</div>sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px).Dark Mode
dark: prefix to any class:<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<h2 class="text-blue-600 dark:text-blue-400">Title</h2>
</div>prefers-color-scheme media query. If you want manual toggle control, set the dark mode strategy to selector in your config and toggle a dark class on the element.Hover, Focus, and Other States
<button class="bg-blue-600 hover:bg-blue-700 focus:ring-2 focus:ring-blue-500
active:bg-blue-800 disabled:opacity-50 disabled:cursor-not-allowed">
Submit
</button><div class="group cursor-pointer">
<h3 class="group-hover:text-blue-600 transition-colors">Card Title</h3>
<p class="group-hover:text-gray-700">Description</p>
</div>@apply: Use Sparingly
@apply to extract utility patterns into CSS classes:.btn-primary {
@apply px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700
focus:ring-2 focus:ring-blue-500 transition-colors;
}@apply class, you're just writing CSS with extra steps and losing the benefits of utility-first.
Bad uses of @apply:
If you're using a component framework (and you probably are), the component itself is your abstraction. You don't need .card, .header, .sidebar classes — use components instead.card when you have .
Tailwind vs Traditional CSS: The Real Trade-off
with 12 Tailwind classes is harder to read than . But a codebase with Tailwind is easier to maintain than a codebase with 8,000 lines of custom CSS — because every style is local, deletable, and obviously connected to its element.Going Further