Tailwind CSS: Why I Use a Utility-First Framework for This Blog
Leroy · 1 Jun 2026 · 6 min read
I used vanilla CSS for years. It worked. I tried Bootstrap, which felt heavy. I tried CSS-in-JS, which felt clever but added complexity. I assumed Tailwind was another trend, until I used it.
Now it powers this blog's design. Here is why.
What is Tailwind CSS?
Tailwind is a utility-first CSS framework. Instead of writing custom classes like .card or .btn-primary, you compose styles in HTML using small utility classes:
<div class="flex items-center gap-4 border rounded-lg p-4 bg-card">
<p class="text-sm text-muted-foreground">Hello</p>
</div>
Each class does one thing.
flexsetsdisplay: flexitems-centeraligns items verticallygap-4addsgap: 1remborderadds a1px solidborderrounded-lgrounds the cornersp-4addspadding: 1rembg-cardsets the background to the card color variable
No naming classes. No switching between HTML and CSS files. No specificity fights.
Why I Use Tailwind
1. No Name Fatigue
The hardest part of CSS is naming things. Is it .card or .post-card or .article-card? What about the one with an image?
With Tailwind I do not name anything. I write styles inline. It sounds messy. In practice I spend zero time on class names and all my time on the actual design.
2. Consistent Design Tokens
Tailwind enforces a design system by default. You do not write #333 or 14px in random places. Every value comes from a predefined scale.
/* Instead of: */
font-size: 14px;
margin-top: 12px;
/* You write: */
text-sm
mt-3
Spacing and typography stay consistent across the whole site. text-sm is always 0.875rem (14px). mt-3 is always 0.75rem (12px). No magic numbers.
3. Custom Theme Variables
Tailwind v4 lets you define CSS variables and map them into the framework:
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-accent: var(--accent);
}
These become utility classes I use everywhere:
<div class="bg-card text-foreground">...</div>
<span class="text-accent">Highlighted text</span>
The values come from CSS custom properties that change between light and dark mode:
:root {
--accent: oklch(0.88 0.01 260);
}
.dark {
--accent: oklch(0.3 0.06 280);
}
One class, bg-accent, works in both themes. No media queries needed.
4. Dark Mode Without the Pain
Dark mode with vanilla CSS means duplicating styles or writing @media (prefers-color-scheme: dark) blocks. With Tailwind I toggle a .dark class on the <html> element, and all the custom properties swap automatically.
function toggleTheme() {
document.documentElement.classList.toggle('dark');
}
Every utility that uses a custom property (bg-card, text-foreground, border-border) adapts instantly. No extra CSS. No media queries.
5. Responsive Layouts
Tailwind's breakpoint prefixes let me write responsive layouts inline:
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
</div>
One line of HTML instead of @media blocks. The breakpoints (sm, md, lg) are consistent across the whole codebase.
6. The Typography Plugin
This blog uses Tailwind's typography plugin (@tailwindcss/typography) to style Markdown content:
<section class="prose lg:prose-xl">{{ .Content }}</section>
One class gives me typography for blog posts: heading hierarchy, readable line heights, styled blockquotes and code blocks. I override specific tokens for my theme:
:root {
--tw-prose-body: var(--foreground);
--tw-prose-headings: var(--foreground);
--tw-prose-links: var(--primary);
}
Blog posts are Markdown files rendered to HTML by Goldmark (a Go library). The prose class makes them look hand-crafted.
7. Small Production CSS
I used to think utility classes meant bloated CSS. Tailwind scans your HTML files and only includes the classes you actually use:
@source "../../templates/";
The compiled style.css for this blog is under 15KB gzipped. That is smaller than most hand-written CSS files.
8. v4 Is CSS-First
Tailwind v4 moved configuration from tailwind.config.js into CSS itself:
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@custom-variant dark (&:is(.dark *));
@theme inline {
--font-sans: "Manrope", sans-serif;
}
No separate configuration file. The CSS file is the single source of truth. No YAML, no JSON, no JavaScript config.
9. It Works with Any Backend
Tailwind is just CSS. It does not care about your framework. This blog uses Go's html/template, and Tailwind works because it is purely a CSS pipeline:
bun run css:build
# Runs: tailwindcss -i ./static/css/input.css -o ./static/css/style.css
The output is a static CSS file served by Go's http.FileServer. No JavaScript bundler needed. No framework lock-in.
10. Component Frameworks
This blog uses raw HTML templates. But Tailwind shines in component frameworks like React, Svelte, and Vue.
In a component framework you are already colocating markup, logic, and styles. Tailwind fits because each component defines its own classes inline, removing the need for separate CSS files per component:
function Card({ title, children }) {
return (
<div className="border rounded-lg p-4 bg-card hover:shadow-sm transition">
<h3 className="text-lg font-semibold">{title}</h3>
<p className="text-sm text-muted-foreground mt-2">{children}</p>
</div>
);
}
<script>
export let title;
</script>
<div class="border rounded-lg p-4 bg-card">
<h3 class="text-lg font-semibold">{title}</h3>
<slot />
</div>
<template>
<div class="border rounded-lg p-4 bg-card">
<h3 class="text-lg font-semibold">{{ title }}</h3>
<slot />
</div>
</template>
With vanilla CSS in a component world you need class names for every variant (.card, .card--large, .card--featured, .card__title, .card__body). Then manage CSS imports, watch for collisions, and deal with specificity when overriding. Tailwind removes all of that.
No naming, no imports, no collisions. Each component is self-contained. You can move, rename, or delete a component without hunting through CSS files for orphaned styles.
A Note on Raw HTML
I should be honest. Tailwind is less enjoyable in a raw HTML codebase like this blog. When you write class="border rounded-lg p-4 bg-card text-sm text-muted-foreground" for the fiftieth time across separate template files, you feel the repetition. There is no component to encapsulate it, no props to vary, just copy-paste.
In Go's html/template I cannot define a reusable Card component like I would in React. I repeat the utility classes in every template that needs a card. When I tweak the card design I need to find and update every instance across multiple files.
Is it still better than writing vanilla CSS? Yes, for me the consistency and design tokens are worth it. But the friction is real. I would not recommend Tailwind for a site built entirely with raw HTML includes unless you can abstract repeated patterns (partials, macros). If your stack does not support component abstractions, traditional CSS with well-named classes might serve you better.
For component frameworks (React, Svelte, Vue) this problem disappears. Each component is written once, colocated, and reused everywhere.
What I Do Not Love
No tool is perfect.
- Long class strings. A complex element can have 10+ classes. It is readable but ugly.
- Repetition in raw HTML. As mentioned above, without components you repeat yourself.
- Learning curve. You need to learn the utility names.
items-centerisalign-items: center.justify-betweenisjustify-content: space-between. It becomes intuitive but takes time. - Not for everyone. If you prefer writing semantic class names and styling in CSS files, Tailwind will feel wrong. That is fine.
What Not to Use
Before Tailwind I considered other approaches.
| Approach | Why I did not choose it |
|---|---|
| Vanilla CSS | Lots of repetition, naming fatigue, specificity management |
| Bootstrap | Heavy, opinionated components, hard to customize, sites look the same |
| CSS-in-JS | Requires a JavaScript framework (React, Vue), adds runtime overhead |
| SCSS / Sass | Preprocessor-only, no design system enforcement, still need to name things |
| Open Props | Interesting design tokens but no utility class system, less ergonomic |
Tailwind hits the sweet spot: utility classes for rapid development, design tokens for consistency, zero runtime, works with any backend.
The Bottom Line
Tailwind CSS changed how I think about styling. I do not write CSS the old way anymore. I compose it. The result is a site that is consistent, responsive, and easy to maintain, with a small CSS footprint and zero JavaScript overhead.
This blog uses Tailwind v4 with custom Catppuccin-inspired theme variables, the typography plugin for Markdown content, and a dark mode toggle that swaps all colors in a single line of JavaScript. The entire design system lives in one input.css file.