learn.colinkim.dev

Utility-first CSS with Tailwind

Learn what utility-first CSS is, how Tailwind CSS v4 works, and when it helps compared to writing CSS from scratch.

Tailwind CSS is a utility-first CSS framework. It provides small, single-purpose classes that you combine in HTML to build interfaces, rather than writing custom CSS in separate files.

Why utility-first exists

Writing custom CSS for every component takes time and discipline. Small teams often end up with:

  • Duplicated styles across component files
  • Unused CSS from deleted features
  • Specificity battles as the codebase grows
  • Inconsistent spacing, color, and typography values

Utility-first CSS flips the approach: provide a complete set of small building blocks in advance, then combine them inline.

How it works

Instead of writing CSS:

.button {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 1rem;
  background: royalblue;
  color: white;
  border-radius: 0.375rem;
}

.button:hover {
  background: darkblue;
}

You apply pre-built utility classes:

<button
  class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-800 transition-colors"
>
  Submit
</button>

Each class does one thing:

  • inline-flex sets display: inline-flex
  • items-center sets align-items: center
  • px-4 sets padding-inline: 1rem
  • bg-blue-600 sets background-color to a specific blue
  • hover:bg-blue-800 changes the background on hover

What Tailwind provides

Tailwind is a design system encoded as CSS classes:

Layout utilities like flex, grid, block, hidden, col-span-2, gap-4

Spacing utilities like p-4, px-2, m-auto, space-y-2

Sizing utilities like w-full, h-screen, min-h-0, max-w-md

Typography utilities like text-sm, font-bold, leading-relaxed, tracking-tight

Color utilities like text-red-500, bg-slate-100, border-gray-200

Effects utilities like shadow-md, rounded-lg, opacity-75, transition

Each utility maps to a specific CSS declaration with a value from Tailwind’s theme.

Composing utilities

Complex components emerge from combining utilities:

<article class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
  <h2 class="text-lg font-semibold text-gray-900">
    Card Title
  </h2>
  <p class="mt-2 text-sm text-gray-600">
    Card description goes here.
  </p>
  <div class="mt-4 flex gap-3">
    <button class="rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-700">
      Primary
    </button>
    <button class="rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-50">
      Secondary
    </button>
  </div>
</article>

The classes describe exactly what the element looks like. There is no indirection through custom class names.

Responsive prefixes

Tailwind uses prefixes for responsive behavior:

<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
  <!-- 1 column on small screens, 2 on medium, 3 on large -->
</div>

The unprefixed version applies to all breakpoints. Prefixed versions only apply at that breakpoint and above. This is mobile-first by default.

State variants

Tailwind provides prefixes for states:

<button class="bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 active:bg-blue-800 disabled:opacity-50 disabled:cursor-not-allowed">
  Interactive button
</button>

Common variants include hover, focus, active, disabled, checked, first, last, odd, even, and group-hover.

Arbitrary values

When Tailwind’s theme does not have the value you need, use arbitrary values:

<div class="h-[calc(100vh-4rem)] w-[120px] bg-[#1da1f2]">
  <!-- exact custom values -->
</div>

Brackets let you escape Tailwind’s theme with actual CSS values. Use sparingly—prefer extending the theme for values you reuse.

What about file size?

Tailwind uses a build step that scans your HTML (or component files) and removes unused utilities. The resulting CSS only contains what you actually used, regardless of how large the full utility set is.

This means the CSS file size scales with your usage, not the framework size.

Comparison to custom CSS

| Custom CSS | Tailwind utilities | |------------|-------------------| | Class names describe components | Class names describe styles | | Styles defined in CSS files | Styles defined in HTML | | Full CSS flexibility | Constrained to Tailwind’s theme | | Requires naming discipline | No naming required | | Can have unused CSS | Automatically removes unused | | Familiar to all CSS developers | Requires learning utility names |

Both approaches can produce good results. The choice depends on team size, project complexity, and maintenance preferences.

When Tailwind fits

Consider Tailwind when:

  • You want rapid prototyping without writing CSS files
  • Your team struggles with CSS architecture decisions
  • You prefer colocating styles with markup
  • You are building within a known design system
  • You want consistent spacing, color, and type scales without enforcing them

When custom CSS fits

Consider custom CSS when:

  • You need design flexibility outside any framework’s constraints
  • Your project has unusual visual requirements
  • You prefer separation of concerns between markup and style
  • You are writing CSS for a library or framework where markup is not under your control
  • Your team has strong opinions about CSS architecture

What to carry forward

  • Utility-first CSS means composing small, single-purpose classes instead of writing custom CSS
  • Tailwind provides a complete set of utilities organized by category
  • Classes describe appearance, not component identity
  • Responsive prefixes (like md:) and state variants (like hover:) add conditional behavior
  • Tailwind removes unused utilities at build time
  • Arbitrary values handle one-off custom needs

The next lesson covers Tailwind’s modern setup, theme customization, and how to extend it for your own design tokens.

Quick Check

One answer

What does a utility-first approach primarily change about how you write styles?

Choose the best answer and use it to track your progress through the lesson.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.