What is Tailwind CSS?
Tailwind CSS is a utility-first CSS framework. Instead of writing CSS in a separate file and inventing class names for every element, you style elements directly in your HTML using small, single-purpose classes called utilities.
Each utility does exactly one job:
| Utility class | The CSS it applies |
|---|---|
p-4 | padding: 1rem; |
text-center | text-align: center; |
bg-teal-600 | background-color: #0d9488; |
rounded-lg | border-radius: 0.5rem; |
flex | display: flex; |
shadow-md | a medium box-shadow |
You compose a design by stacking these small classes, the same way you build sentences from words.
The same card, two ways
Traditional CSS — you switch between two files and invent names like .profile-card:
<div class="profile-card">
<h2 class="profile-card-title">Rohit Jangra</h2>
<p class="profile-card-role">Full-Stack Developer</p>
</div>
<style>
.profile-card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.profile-card-title { font-size: 20px; font-weight: 700; color: #0f172a; }
.profile-card-role { font-size: 14px; color: #64748b; margin-top: 4px; }
</style>
Tailwind — everything lives on the element itself:
<div class="bg-white rounded-xl p-6 shadow-md max-w-xs">
<h2 class="text-xl font-bold text-slate-900">Rohit Jangra</h2>
<p class="text-sm text-slate-500 mt-1">Full-Stack Developer</p>
</div>
At first this looks like "inline styles with extra steps". It is not — and the difference matters:
- Utilities are constrained.
p-6comes from a fixed spacing scale. Inline styles allowpadding: 23.5px; Tailwind nudges every developer on the team toward the same consistent values. - Utilities support states and breakpoints. You can write
hover:bg-teal-500ormd:p-8. Inline styles simply cannot do hover, focus or media queries. - No naming fatigue. No more inventing
.card-inner-wrapper-2. Naming things is famously one of the hardest problems in programming — Tailwind removes 90% of it. - Deleting is safe. Delete the HTML and its styling goes with it. With CSS files, dead styles pile up because nobody is sure what still uses them.
Why "the CSS file stays small"
Tailwind generates only the classes you actually use. At build time it scans your HTML/JSX/PHP templates, finds every class name, and produces a stylesheet containing just those rules. A typical production Tailwind stylesheet is under 10 kB compressed — smaller than most hand-written CSS that has grown for a year.
Three ways to set up Tailwind
1. Play CDN — fastest (learning & prototypes)
Add one script tag to any HTML file:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-slate-100">
<h1 class="text-3xl font-bold text-teal-700 p-8">Tailwind is working!</h1>
</body>
</html>
The CDN build compiles the classes you use in the browser at runtime. Perfect for learning (this is exactly what our compiler's Tailwind mode uses), demos and quick experiments — but not for production, because it ships the whole compiler to every visitor.
Every example in this course runs in the built-in compiler. Press Interactive Editor on any lesson, change a class, hit Run and watch the preview update. You learn Tailwind by changing classes, not by reading about them.
2. Tailwind CLI — simple real projects
npm install -D tailwindcss
npx tailwindcss init
This creates tailwind.config.js. Tell it where your HTML lives, add the three directives to an input CSS file, and run the watcher:
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch
/* src/input.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
The CLI watches your files and rebuilds output.css on every save — link that file in your HTML like any stylesheet.
3. Vite / React / Next.js — modern app development
npm create vite@latest my-app
cd my-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Then set the content paths in tailwind.config.js so the scanner knows which files to read:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: { extend: {} },
plugins: [],
};
Import the CSS once in your entry file and every component can use utilities.
How the JIT engine works (mental model)
Tailwind's Just-In-Time engine is the reason arbitrary values like w-[137px] or bg-[#1e293b] work. The flow:
- The scanner reads every file listed in
contentas plain text. - Anything that looks like a class name is collected — which is why you must never build class names by string concatenation (
"text-" + coloris invisible to the scanner; write the full classtext-red-500somewhere in the file). - For each recognised class, the engine generates exactly one CSS rule.
- States and breakpoints become wrapped rules —
md:hover:bg-teal-500compiles to a media query containing a:hoverrule.
Preflight: why your headings suddenly look plain
The moment Tailwind loads, an <h1> looks the same as a <p>. That is Preflight — Tailwind's base reset. It removes default margins, heading sizes, list bullets and link colours so that you decide every visual choice explicitly and browsers stop disagreeing with each other.
| Preflight change | Why it helps |
|---|---|
| All heading sizes reset | text-4xl font-bold states your intent — no hidden browser defaults |
| Margins zeroed | Spacing comes only from your m- / space-y- utilities |
| Lists unstyled | Navigation lists no longer need "remove bullet" CSS |
Images display: block | Kills the mysterious 4px gap under every image |
| Borders normalised | border utility gives a clean 1px solid line everywhere |
So when you write your first Tailwind page and everything looks "unstyled" — that is by design. You are starting from a truly blank canvas.
Your first real component, step by step
Watch how a button evolves one utility at a time — this is the workflow you will use every day:
<div class="p-8 space-y-4 bg-slate-100 min-h-screen">
<!-- Step 1: just text -->
<button>Enroll Now</button>
<!-- Step 2: colour + padding -->
<button class="bg-teal-600 text-white px-4 py-2">Enroll Now</button>
<!-- Step 3: rounded corners + font weight -->
<button class="bg-teal-600 text-white px-4 py-2 rounded-lg font-semibold">Enroll Now</button>
<!-- Step 4: hover state + shadow + smooth transition -->
<button class="bg-teal-600 hover:bg-teal-500 text-white px-4 py-2 rounded-lg font-semibold shadow-md transition-colors">
Enroll Now
</button>
</div>
Read step 4 aloud: background teal-600, on hover teal-500, white text, padding x 4 y 2, rounded large, semibold, medium shadow, animate colour changes. The class list is the design specification — any developer can read the exact styling without opening another file.
Common beginner mistakes
- Building class names dynamically.
class="text-{{ color }}-500"produces classes the scanner never sees → no CSS is generated. Always write complete class names. - Using the CDN in production. It works, but ships a full compiler to the browser. Use the CLI or a build tool for anything real.
- Fighting Preflight. Beginners re-add browser defaults with custom CSS. Instead, style headings intentionally (
text-3xl font-bold mb-4) — after a week it becomes second nature. - Wrong
contentglobs. If a file is not matched bycontent, its classes silently produce no CSS. If "my class does nothing", check this first.
Practice task
In the Interactive Editor below: (1) change the card's background from white to bg-slate-900 and fix the text colours for dark, (2) make the button full-width with w-full, (3) change the accent from teal to indigo everywhere. Three edits, zero CSS files.