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-flexsetsdisplay: inline-flexitems-centersetsalign-items: centerpx-4setspadding-inline: 1rembg-blue-600setsbackground-colorto a specific bluehover:bg-blue-800changes 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 (likehover:) 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 answerWhat 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.
Why that answer is correct
Utility-first moves style definitions from CSS files into HTML via composable utility classes, rather than creating custom class names with associated CSS rules.