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.

  • flex sets display: flex
  • items-center aligns items vertically
  • gap-4 adds gap: 1rem
  • border adds a 1px solid border
  • rounded-lg rounds the corners
  • p-4 adds padding: 1rem
  • bg-card sets 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-center is align-items: center. justify-between is justify-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.

related posts

01 Jun
Tailwind CSS: Why I Use a Utility-First Framework for This Blog
How Tailwind CSS v4 powers this blog's design - from the Catppuccin-inspired dark mode to the responsive card layout - and why utility-first CSS wins for me.
01 Jun
Bouncy Card Hover: A Tiny CSS Detail That Makes a Difference
How a single CSS class with a spring-like cubic-bezier adds a satisfying bounce to every card on this blog - and why small animations matter.
01 Jun
Adding a Gooey Menu Hover Effect with GSAP
How I added a viscous, liquid-like gooey hover effect to the navigation menu using GSAP and an SVG filter - no JavaScript framework required.