Skip to content

Tailwind CSS — A Practical Guide

What this is: a from-scratch guide to Tailwind CSS — what it is, why people use it, how to install and use it, and the patterns you'll meet daily. Written for engineers and QAs who can read HTML/CSS but are new to the "utility-first" approach.


1. What Is Tailwind CSS?

Tailwind CSS is a utility-first CSS framework. Instead of giving you pre-built components (like a "card" or "button" class as Bootstrap does), it gives you thousands of tiny single-purpose utility classes that you compose directly in your HTML to build any design.

<!-- Traditional CSS: write a class, then style it in a .css file -->
<button class="btn-primary">Save</button>

<!-- Tailwind: compose utilities inline -->
<button class="bg-teal-600 text-white font-semibold px-4 py-2 rounded-lg hover:bg-teal-700">
  Save
</button>

Each class does exactly one thing: bg-teal-600 sets the background colour, px-4 sets horizontal padding, rounded-lg rounds the corners, hover:bg-teal-700 darkens on hover.

flowchart LR
    A["Traditional CSS<br/>semantic classes<br/>(.card, .btn)"] -->|"write custom CSS<br/>for every component"| B["growing .css files"]
    C["Tailwind<br/>utility classes<br/>(px-4, bg-teal-600)"] -->|"compose in markup<br/>no custom CSS"| D["styles live with the HTML"]

2. Why Use It? (and when not to)

Strengths Trade-offs
No naming fatigue — no inventing .card-header-inner-wrapper class names Verbose markup — long class="..." strings
Styles co-located with markup — no jumping between files Learning curve — you must learn the utility vocabulary
Consistent design system — spacing/colour come from a fixed scale Can look "ugly" in raw HTML diffs
Tiny production CSS — unused classes are stripped out automatically Best paired with components to avoid repetition
Responsive & state variants built in (md:, hover:, dark:) Not ideal for tiny static pages where plain CSS is simpler

Use Tailwind when: building component-based apps (React, Vue, Svelte), rapid prototyping, or any project where a consistent design scale matters. Skip it when: a one-page static site where a few lines of plain CSS would do, or a project already committed to a different styling approach.

Note for this portal: this very site is built with Material for MkDocs, which uses traditional CSS (see our extra.css), not Tailwind. Tailwind shines in app frameworks — the contrast is a good way to understand both approaches.


3. The Mental Model — Utility-First

You stop thinking "what component is this?" and start thinking "what does it look like?" — then spell that out with utilities.

<div class="max-w-sm rounded-xl shadow-md bg-white p-6">
  <h2 class="text-xl font-bold text-slate-900">Pricing</h2>
  <p class="mt-2 text-sm text-slate-600">Simple, transparent pricing.</p>
  <button class="mt-4 w-full bg-indigo-600 text-white py-2 rounded-lg
                 hover:bg-indigo-700 transition">
    Get started
  </button>
</div>

Read it like a sentence: max-width small, extra-rounded corners, medium shadow, white background, padding 6 → a heading that's extra-large, bold, dark slate → a paragraph with top margin, small, muted → a button…


4. Installation & Setup

Tailwind ships as a build tool that scans your files and generates only the CSS you actually use. The modern setup (Tailwind v4) is the simplest it has ever been.

npm install tailwindcss @tailwindcss/vite
// vite.config.js
import tailwindcss from '@tailwindcss/vite';
export default { plugins: [tailwindcss()] };
/* src/style.css — a single import is all v4 needs */
@import "tailwindcss";

Then import that CSS in your app entry point and start using classes.

Option B — Tailwind CLI (no framework)

npm install tailwindcss @tailwindcss/cli
npx @tailwindcss/cli -i ./src/input.css -o ./dist/output.css --watch
/* input.css */
@import "tailwindcss";
<link href="/dist/output.css" rel="stylesheet" />

Option C — Play CDN (prototyping only, never production)

<script src="https://cdn.tailwindcss.com"></script>
<h1 class="text-3xl font-bold text-teal-600">Hello Tailwind</h1>

v3 vs v4: older projects (v3) use a tailwind.config.js file plus three @tailwind base; @tailwind components; @tailwind utilities; directives. v4 replaces that with the single @import "tailwindcss"; and CSS-based configuration. If you see @tailwind directives in a codebase, it's v3.


5. The Core Utility Vocabulary

You don't memorise all of them — you learn the prefixes and the scale. Most numeric utilities use a spacing scale where 1 = 0.25rem (4px), so p-4 = 16px padding.

Spacing — margin & padding

Class Effect
p-4 padding: 1rem (all sides)
px-6 / py-2 horizontal / vertical padding
pt-3 pr-2 pb-3 pl-2 individual sides
m-4 / mx-auto margin / center horizontally
gap-4 gap between flex/grid children
space-y-4 vertical spacing between children

Typography

Class Effect
text-sm text-lg text-3xl font size
font-bold font-medium font weight
text-center text-right alignment
text-slate-600 colour (palette + shade 50–950)
leading-relaxed line height
tracking-tight letter spacing
uppercase truncate transforms

Colours

Tailwind ships a full palette: slate, gray, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, pink, rose — each with shades 50 (lightest) → 950 (darkest).

<p class="text-red-500 bg-red-50 border border-red-200">Error</p>
<span class="text-teal-700">Brand colour</span>

Prefixes: text- (font), bg- (background), border- (border), ring- (focus ring), from-/to- (gradients).

Sizing

Class Effect
w-full w-1/2 w-64 width (full, fraction, fixed)
h-screen h-10 height
max-w-md min-h-screen constraints

6. Layout — Flexbox & Grid

This is where Tailwind saves the most time.

Flexbox

<!-- horizontal row, items centered, space between -->
<div class="flex items-center justify-between">
  <span class="font-semibold">Logo</span>
  <nav class="flex gap-4">
    <a href="#">Home</a>
    <a href="#">Docs</a>
  </nav>
</div>
Class Effect
flex display: flex
flex-col stack vertically
items-center align cross-axis center
justify-between justify-center main-axis distribution
flex-wrap allow wrapping
flex-1 grow to fill

Grid

<!-- responsive card grid: 1 col on mobile, 3 on desktop -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
  <div class="...">Card</div>
  <div class="...">Card</div>
  <div class="...">Card</div>
</div>
Class Effect
grid display: grid
grid-cols-3 3 equal columns
col-span-2 item spans 2 columns
gap-6 gap between cells

7. Responsive Design — Mobile-First Breakpoints

Tailwind is mobile-first: an unprefixed utility applies to all sizes; a prefixed one applies from that breakpoint up.

<div class="text-base md:text-lg lg:text-xl">
  Small on phones, larger on tablets, largest on desktop
</div>
Prefix Applies from Typical device
(none) 0px mobile
sm: 640px large phone
md: 768px tablet
lg: 1024px laptop
xl: 1280px desktop
2xl: 1536px large desktop

Key mindset: style the mobile layout first with unprefixed classes, then add md:/lg: overrides for bigger screens — not the other way around.


8. State & Conditional Variants

Prefix any utility to make it conditional. They stack.

<button class="bg-teal-600 hover:bg-teal-700 focus:ring-2 focus:ring-teal-300
               active:scale-95 disabled:opacity-50 transition">
  Submit
</button>

<!-- dark mode -->
<div class="bg-white text-slate-900 dark:bg-slate-800 dark:text-slate-100">
  Adapts to dark mode
</div>

<!-- style children / siblings -->
<div class="group">
  <p class="text-slate-500 group-hover:text-teal-600">Hover the parent</p>
</div>
Variant Meaning
hover: focus: active: interaction states
disabled: checked: form states
dark: dark colour scheme
md: lg: responsive (combinable: md:hover:)
group-hover: react to a parent's hover
first: last: odd: even: structural

9. Avoiding Repetition — Components & @apply

Long class lists repeated everywhere is the #1 Tailwind complaint. Two solutions:

Extract a component (the preferred way)

In a framework, wrap the markup once and reuse it:

// Button.jsx — define the classes once
function Button({ children }) {
  return (
    <button className="bg-teal-600 text-white font-semibold px-4 py-2
                       rounded-lg hover:bg-teal-700 transition">
      {children}
    </button>
  );
}
// usage: <Button>Save</Button>

@apply — fold utilities into a CSS class

For plain HTML or shared primitives:

.btn-primary {
  @apply bg-teal-600 text-white font-semibold px-4 py-2 rounded-lg
         hover:bg-teal-700 transition;
}
<button class="btn-primary">Save</button>

Guidance: prefer component extraction over @apply. Overusing @apply recreates the very "custom CSS soup" Tailwind aims to avoid — reach for it only for small, truly reused primitives.


10. Customisation — The Theme

Tailwind's scales (colours, spacing, fonts) are a design system you can extend. In v4, you configure in CSS with @theme:

@import "tailwindcss";

@theme {
  --color-brand: #0d9488;          /* enables bg-brand, text-brand, etc. */
  --font-display: "Inter", sans-serif;
  --spacing-18: 4.5rem;            /* enables p-18, m-18 */
}
<h1 class="text-brand font-display">Branded heading</h1>

For arbitrary one-off values, use square-bracket notation (no config needed):

<div class="top-[117px] bg-[#1da1f2] grid-cols-[1fr_500px_2fr]">
  Exact values when the scale doesn't have what you need
</div>

11. A Complete Example — A Responsive Card

Putting it together — a production-style component with layout, responsive behaviour, states, and dark mode:

<article class="max-w-sm mx-auto bg-white dark:bg-slate-800 rounded-2xl
                shadow-md hover:shadow-xl transition-shadow overflow-hidden">
  <img src="cover.jpg" alt="" class="h-40 w-full object-cover" />
  <div class="p-6">
    <span class="inline-block text-xs font-bold uppercase tracking-wide
                 text-teal-700 bg-teal-50 px-2 py-1 rounded-full">
      Tutorial
    </span>
    <h3 class="mt-3 text-lg font-bold text-slate-900 dark:text-white">
      Getting started with Tailwind
    </h3>
    <p class="mt-2 text-sm text-slate-600 dark:text-slate-300 leading-relaxed">
      A utility-first workflow that scales from prototype to production.
    </p>
    <a href="#" class="mt-4 inline-flex items-center gap-1 text-sm font-semibold
                       text-teal-600 hover:text-teal-700">
      Read more →
    </a>
  </div>
</article>

12. Tooling & Testing Notes

  • IntelliSense: install the official Tailwind CSS IntelliSense VS Code extension — autocomplete, hover previews, and linting for class names.
  • Prettier plugin: prettier-plugin-tailwindcss auto-sorts class names into a canonical order, keeping diffs clean.
  • For QA / automation: Tailwind classes are presentational, so never use them as test selectors — a restyle (bg-teal-600bg-emerald-600) would break your tests. Select by data-testid, ARIA role, or text instead. See API Testing with Playwright and Playwright Framework Tutorial for robust selector practices.
  • Visual regression: because Tailwind centralises the design scale, visual-regression snapshots are stable as long as the theme tokens don't change — a good fit for screenshot diffing.

13. Quick Reference

Need Utility
Padding / margin p-4 px-6 m-2 mx-auto
Text size / weight text-lg font-bold
Colour text-slate-700 bg-teal-600
Flexbox flex items-center justify-between gap-4
Grid grid grid-cols-3 gap-6
Sizing w-full max-w-md h-screen
Rounding / shadow rounded-lg shadow-md
Responsive md:flex lg:text-xl
States hover: focus: disabled: dark:
Arbitrary value w-[137px] bg-[#0d9488]
Reuse component extraction or @apply

14. Where to Go Next