Tailwind CSS Tutorial for Beginners (2025)
Tailwind CSS is a utility-first CSS framework that lets you build any design directly in your HTML. Instead of writing custom CSS, you compose pre-built utility classes.
This tutorial takes you from zero to building production-ready UIs with Tailwind CSS 3.x.
Why Learn Tailwind CSS?
| Reason | Detail |
|---|---|
| No context switching | Style in HTML, no separate CSS files |
| No naming | No inventing class names like .card-header-inner-wrapper |
| Fast iteration | Change styles without touching CSS files |
| Responsive built-in | md:flex lg:grid prefixes for breakpoints |
| Dark mode built-in | dark:bg-gray-900 without custom media queries |
| Tiny production bundle | Purges unused classes — typically < 10KB |
| Highly adoptable | Works with React, Vue, Angular, plain HTML |
| Job market | Used by Vercel, GitHub, OpenAI, Shopify |
Tailwind vs Bootstrap vs Custom CSS
| Feature | Tailwind CSS | Bootstrap | Custom CSS |
|---|---|---|---|
| Approach | Utility-first | Component-first | Write-your-own |
| Bundle size (dev) | Large (all classes) | ~150KB | Varies |
| Bundle size (prod) | < 10KB (purged) | ~30KB | Varies |
| Design uniqueness | Unique — no default look | Bootstrap look | Fully custom |
| Learning curve | Medium | Low | High |
| Customization | Deep (tailwind.config.js) |
Limited overrides | Unlimited |
| Responsive | sm:, md:, lg:, xl:, 2xl: |
Grid + breakpoints | Media queries |
| Dark mode | dark: prefix |
Limited | prefers-color-scheme |
| Component library | shadcn/ui, Headless UI | Built-in components | DIY |
| Maintenance | Rename class in HTML | Override specificity | Stylesheet debt |
Setup
Option 1: CDN (For Learning / Prototyping)
No install needed:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tailwind CSS</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-8">
<h1 class="text-3xl font-bold text-blue-600">Hello, Tailwind!</h1>
<p class="mt-4 text-gray-700">This is Tailwind CSS via CDN.</p>
</body>
</html>
CDN is great for learning, but NOT for production — it loads all classes (3MB+). Use the CLI for real projects.
Option 2: Tailwind CLI (Recommended for HTML projects)
# Install Tailwind
npm install -D tailwindcss
npx tailwindcss init
# Input CSS (src/input.css)
# Output CSS (dist/output.css)
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch
Your src/input.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
Your tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
Option 3: Vite + React (Most Common in 2025)
npm create vite@latest my-app -- --template react
cd my-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Update tailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Add to src/index.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
Import in src/main.jsx:
import './index.css'
Run:
npm run dev
Option 4: Next.js
npx create-next-app@latest my-app --tailwind
# Tailwind is automatically configured
Project Structure
my-app/
├── src/
│ ├── index.css # Tailwind directives
│ ├── main.jsx
│ └── App.jsx
├── tailwind.config.js # Tailwind configuration
├── postcss.config.js # PostCSS (Vite sets this up)
└── package.json
Core Concepts
Utility Classes — The Tailwind Way
Instead of writing CSS, you apply utility classes directly in HTML:
<!-- Traditional CSS -->
<style>
.card {
background-color: white;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
padding: 24px;
max-width: 320px;
}
</style>
<div class="card">...</div>
<!-- Tailwind Way -->
<div class="bg-white rounded-lg shadow p-6 max-w-sm">...</div>
The Tailwind Mental Model
Every Tailwind class maps to one or a few CSS properties:
| Utility | CSS |
|---|---|
text-blue-600 |
color: rgb(37 99 235) |
bg-gray-100 |
background-color: rgb(243 244 246) |
p-4 |
padding: 1rem |
flex |
display: flex |
rounded-lg |
border-radius: 0.5rem |
shadow |
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) |
font-bold |
font-weight: 700 |
w-full |
width: 100% |
hidden |
display: none |
uppercase |
text-transform: uppercase |
Spacing
Tailwind uses a spacing scale. 1 unit = 0.25rem = 4px (default).
Padding
<div class="p-4">16px all sides</div>
<div class="px-4">16px left + right (x-axis)</div>
<div class="py-4">16px top + bottom (y-axis)</div>
<div class="pt-2 pr-4 pb-2 pl-4">individual sides</div>
<div class="ps-4">padding-inline-start (RTL aware)</div>
Margin
<div class="m-4">16px all sides</div>
<div class="mx-auto">center horizontally</div>
<div class="mt-8 mb-4">top 32px, bottom 16px</div>
<div class="-mt-2">negative margin (pull up 8px)</div>
Spacing Scale
| Class | Value |
|---|---|
0 |
0px |
0.5 |
2px |
1 |
4px |
2 |
8px |
3 |
12px |
4 |
16px |
5 |
20px |
6 |
24px |
8 |
32px |
10 |
40px |
12 |
48px |
16 |
64px |
20 |
80px |
24 |
96px |
32 |
128px |
px |
1px |
Typography
Font Size
<p class="text-xs">12px</p>
<p class="text-sm">14px</p>
<p class="text-base">16px (default)</p>
<p class="text-lg">18px</p>
<p class="text-xl">20px</p>
<p class="text-2xl">24px</p>
<p class="text-3xl">30px</p>
<p class="text-4xl">36px</p>
<p class="text-5xl">48px</p>
<p class="text-6xl">60px</p>
Font Weight
<p class="font-thin">100</p>
<p class="font-light">300</p>
<p class="font-normal">400</p>
<p class="font-medium">500</p>
<p class="font-semibold">600</p>
<p class="font-bold">700</p>
<p class="font-extrabold">800</p>
<p class="font-black">900</p>
Text Color
<p class="text-gray-900">near black</p>
<p class="text-gray-600">medium gray</p>
<p class="text-gray-400">light gray</p>
<p class="text-blue-600">blue</p>
<p class="text-red-500">red</p>
<p class="text-green-600">green</p>
<p class="text-white">white</p>
Other Typography
<p class="leading-tight">line-height: 1.25</p>
<p class="leading-normal">line-height: 1.5</p>
<p class="leading-loose">line-height: 2</p>
<p class="tracking-tight">letter-spacing: -0.05em</p>
<p class="tracking-wide">letter-spacing: 0.025em</p>
<p class="italic">italic</p>
<p class="underline">underlined</p>
<p class="line-through">strikethrough</p>
<p class="uppercase">UPPERCASE</p>
<p class="capitalize">Capitalize First</p>
<p class="truncate">Long text that gets truncated...</p>
<p class="text-center">centered</p>
<p class="text-right">right-aligned</p>
Colors
Tailwind includes a full color palette. Colors come in shades 50–950.
<!-- Slate -->
<div class="bg-slate-100">Light slate</div>
<div class="bg-slate-800">Dark slate</div>
<!-- Blue -->
<div class="bg-blue-50">Lightest blue</div>
<div class="bg-blue-500">Medium blue</div>
<div class="bg-blue-900">Darkest blue</div>
<!-- All color families: -->
<!-- slate, gray, zinc, neutral, stone -->
<!-- red, orange, amber, yellow, lime -->
<!-- green, emerald, teal, cyan -->
<!-- sky, blue, indigo, violet, purple, fuchsia, pink, rose -->
Opacity Modifier
<div class="bg-blue-500/50">50% opacity blue background</div>
<p class="text-black/75">75% opacity black text</p>
Backgrounds
<div class="bg-white">white</div>
<div class="bg-transparent">transparent</div>
<div class="bg-gradient-to-r from-blue-500 to-purple-600">gradient</div>
<div class="bg-cover bg-center" style="background-image: url(...)">image</div>
Borders
<div class="border">1px border (default color)</div>
<div class="border-2">2px border</div>
<div class="border-4">4px border</div>
<div class="border-blue-500">colored border</div>
<div class="border-dashed">dashed</div>
<div class="border-dotted">dotted</div>
<div class="border-t">top border only</div>
<div class="border-b-2 border-blue-600">bottom border 2px blue</div>
<!-- Border radius -->
<div class="rounded">4px radius</div>
<div class="rounded-md">6px radius</div>
<div class="rounded-lg">8px radius</div>
<div class="rounded-xl">12px radius</div>
<div class="rounded-2xl">16px radius</div>
<div class="rounded-full">50% (pill/circle)</div>
<div class="rounded-t-lg">only top rounded</div>
Sizing
Width
<div class="w-4">16px (1rem)</div>
<div class="w-16">64px (4rem)</div>
<div class="w-64">256px (16rem)</div>
<div class="w-full">100%</div>
<div class="w-screen">100vw</div>
<div class="w-1/2">50%</div>
<div class="w-1/3">33.333%</div>
<div class="w-2/3">66.666%</div>
<div class="w-auto">auto</div>
<div class="min-w-0">min-width: 0</div>
<div class="max-w-xs">max-width: 320px</div>
<div class="max-w-sm">max-width: 384px</div>
<div class="max-w-md">max-width: 448px</div>
<div class="max-w-lg">max-width: 512px</div>
<div class="max-w-xl">max-width: 576px</div>
<div class="max-w-2xl">max-width: 672px</div>
<div class="max-w-4xl">max-width: 896px</div>
<div class="max-w-7xl">max-width: 1280px</div>
Height
<div class="h-16">64px</div>
<div class="h-full">100%</div>
<div class="h-screen">100vh</div>
<div class="h-svh">100svh (safe viewport)</div>
<div class="min-h-screen">min-height: 100vh</div>
Display & Layout
<div class="block">block</div>
<span class="inline-block">inline-block</span>
<div class="hidden">display: none</div>
<div class="flex">flex container</div>
<div class="grid">grid container</div>
<div class="inline-flex">inline flex</div>
Flexbox
<!-- Flex container -->
<div class="flex">
<!-- defaults: row, no-wrap, stretch -->
</div>
<!-- Direction -->
<div class="flex flex-row">horizontal (default)</div>
<div class="flex flex-col">vertical</div>
<div class="flex flex-row-reverse">reverse horizontal</div>
<div class="flex flex-col-reverse">reverse vertical</div>
<!-- Wrap -->
<div class="flex flex-wrap">wrap items</div>
<div class="flex flex-nowrap">no wrap (default)</div>
<!-- Justify (main axis) -->
<div class="flex justify-start">start (default)</div>
<div class="flex justify-center">center</div>
<div class="flex justify-end">end</div>
<div class="flex justify-between">space-between</div>
<div class="flex justify-around">space-around</div>
<div class="flex justify-evenly">space-evenly</div>
<!-- Align (cross axis) -->
<div class="flex items-start">top</div>
<div class="flex items-center">center</div>
<div class="flex items-end">bottom</div>
<div class="flex items-stretch">stretch (default)</div>
<div class="flex items-baseline">baseline</div>
<!-- Gap -->
<div class="flex gap-4">16px gap between items</div>
<div class="flex gap-x-4 gap-y-2">separate x and y gaps</div>
<!-- Flex items -->
<div class="flex-1">flex: 1 1 0% (grow and shrink)</div>
<div class="flex-auto">flex: 1 1 auto</div>
<div class="flex-none">flex: none (no grow/shrink)</div>
<div class="grow">flex-grow: 1</div>
<div class="shrink-0">flex-shrink: 0</div>
Common Flex Patterns
<!-- Center horizontally and vertically -->
<div class="flex items-center justify-center h-screen">
<p>Perfectly centered</p>
</div>
<!-- Navbar: logo left, links right -->
<nav class="flex items-center justify-between p-4">
<div class="text-xl font-bold">Logo</div>
<ul class="flex gap-6">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<!-- Equal width columns -->
<div class="flex gap-4">
<div class="flex-1 bg-blue-100 p-4">Column 1</div>
<div class="flex-1 bg-green-100 p-4">Column 2</div>
<div class="flex-1 bg-red-100 p-4">Column 3</div>
</div>
<!-- Sidebar + Content layout -->
<div class="flex h-screen">
<aside class="w-64 bg-gray-800 text-white p-4">Sidebar</aside>
<main class="flex-1 p-4 overflow-auto">Main Content</main>
</div>
CSS Grid
<!-- Basic grid -->
<div class="grid grid-cols-3 gap-4">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<!-- Auto-fit responsive grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- columns adapt to screen size -->
</div>
<!-- Span columns -->
<div class="grid grid-cols-3 gap-4">
<div class="col-span-2">Takes 2 columns</div>
<div>1 column</div>
</div>
<!-- Span rows -->
<div class="grid grid-cols-2 grid-rows-2 gap-4">
<div class="row-span-2">Tall item</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<!-- Grid template areas -->
<div class="grid grid-cols-[200px_1fr] grid-rows-[auto_1fr_auto] min-h-screen">
<!-- Arbitrary values with [] -->
</div>
Grid vs Flexbox
| Use case | Tool |
|---|---|
| 1D layout (row OR column) | Flexbox |
| 2D layout (rows AND columns) | Grid |
| Navigation bar | Flexbox |
| Card grid | Grid |
| Centering content | Either |
| Magazine layout | Grid |
| Component internals | Flexbox |
| Overall page layout | Grid |
Responsive Design
Tailwind is mobile-first. Unprefixed classes apply at all sizes. Prefixed classes apply at that breakpoint and above.
Breakpoints
| Prefix | Min width | Common use |
|---|---|---|
| (none) | 0px | Mobile (base) |
sm: |
640px | Small tablets |
md: |
768px | Tablets |
lg: |
1024px | Laptops |
xl: |
1280px | Desktops |
2xl: |
1536px | Large screens |
<!-- Hidden on mobile, block on medium+ -->
<div class="hidden md:block">
Desktop content
</div>
<!-- Full width on mobile, half on medium, third on large -->
<div class="w-full md:w-1/2 lg:w-1/3">...</div>
<!-- Stack on mobile, row on desktop -->
<div class="flex flex-col md:flex-row gap-4">
<div>Column A</div>
<div>Column B</div>
</div>
<!-- Font size grows with screen -->
<h1 class="text-2xl md:text-4xl lg:text-6xl font-bold">
Responsive Heading
</h1>
<!-- Grid responsive -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div>Card</div>
<div>Card</div>
<div>Card</div>
<div>Card</div>
</div>
Dark Mode
Enable in tailwind.config.js:
export default {
darkMode: 'class', // or 'media'
// ...
}
'media'— usesprefers-color-schemeOS setting'class'— toggle dark class on<html>(gives you control)
<!-- With class strategy: add 'dark' class to <html> -->
<html class="dark">
<!-- Dark variants -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white p-6">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
Adapts to dark mode
</h1>
<p class="text-gray-600 dark:text-gray-400">
Subtle text that lightens in dark mode
</p>
<button class="bg-blue-600 dark:bg-blue-500 text-white px-4 py-2 rounded">
Button
</button>
</div>
Toggle dark mode with JS:
// Toggle
document.documentElement.classList.toggle('dark')
// React example
const [dark, setDark] = useState(false)
useEffect(() => {
document.documentElement.classList.toggle('dark', dark)
}, [dark])
Hover, Focus, and State Variants
<!-- Hover -->
<button class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded">
Hover me
</button>
<!-- Focus -->
<input class="border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 p-2 rounded" />
<!-- Active -->
<button class="bg-blue-600 active:bg-blue-800 text-white px-4 py-2 rounded">
Click me
</button>
<!-- Disabled -->
<button class="bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed text-white px-4 py-2 rounded" disabled>
Disabled
</button>
<!-- Group hover (parent hover affects child) -->
<div class="group p-4 bg-gray-100 hover:bg-blue-600 rounded">
<p class="text-gray-900 group-hover:text-white">
I change when parent is hovered
</p>
</div>
<!-- Peer (sibling state) -->
<input class="peer border p-2 rounded" placeholder="Type..." />
<p class="hidden peer-focus:block text-sm text-blue-600">
Now typing...
</p>
<!-- First/last child -->
<ul>
<li class="border-b last:border-b-0 py-2">Item 1</li>
<li class="border-b last:border-b-0 py-2">Item 2</li>
<li class="border-b last:border-b-0 py-2">Item 3</li>
</ul>
<!-- Odd/even -->
<tr class="bg-white odd:bg-gray-50">...</tr>
Shadows and Effects
<!-- Box shadow -->
<div class="shadow-sm">Small shadow</div>
<div class="shadow">Default shadow</div>
<div class="shadow-md">Medium shadow</div>
<div class="shadow-lg">Large shadow</div>
<div class="shadow-xl">Extra large</div>
<div class="shadow-2xl">2x large</div>
<div class="shadow-inner">Inner shadow</div>
<div class="shadow-none">No shadow</div>
<div class="shadow-blue-500/50">Colored shadow (with opacity)</div>
<!-- Opacity -->
<div class="opacity-50">50% transparent</div>
<div class="opacity-0">invisible (but takes space)</div>
<!-- Blur -->
<div class="blur">blurred</div>
<div class="blur-sm">small blur</div>
<div class="blur-md">medium blur</div>
<div class="backdrop-blur-sm">blur the background behind</div>
Position
<div class="relative">
<!-- Parent: position: relative -->
<div class="absolute top-0 right-0">
<!-- positioned relative to parent -->
</div>
</div>
<div class="fixed top-0 left-0 w-full">Sticky header</div>
<div class="sticky top-0">Sticky on scroll</div>
<!-- Z-index -->
<div class="z-10">z-index: 10</div>
<div class="z-50">z-index: 50</div>
Transitions and Animations
<!-- Transition all properties -->
<button class="bg-blue-600 hover:bg-blue-700 transition-colors duration-200 text-white px-4 py-2 rounded">
Smooth color change
</button>
<!-- Transition specific properties -->
<div class="transition-transform duration-300 ease-in-out hover:scale-105">
Scale on hover
</div>
<!-- Transition classes -->
<div class="transition-all duration-500 ease-in-out">all properties</div>
<div class="transition-opacity duration-200">opacity only</div>
<div class="transition-shadow">shadow only</div>
<!-- Duration -->
<!-- duration-75, duration-100, duration-150, duration-200,
duration-300, duration-500, duration-700, duration-1000 -->
<!-- Easing -->
<div class="ease-linear">linear</div>
<div class="ease-in">ease-in</div>
<div class="ease-out">ease-out</div>
<div class="ease-in-out">ease-in-out</div>
<!-- Built-in animations -->
<div class="animate-spin">Spinning loader</div>
<div class="animate-ping">Ping effect (badges)</div>
<div class="animate-pulse">Pulsing (skeleton loading)</div>
<div class="animate-bounce">Bouncing</div>
Customization with tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: ["./src/**/*.{html,js,jsx,tsx}"],
theme: {
extend: {
// Add custom colors
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a5f',
},
accent: '#ff6b6b',
},
// Custom fonts
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
heading: ['Poppins', 'sans-serif'],
},
// Custom spacing
spacing: {
'128': '32rem',
'144': '36rem',
},
// Custom breakpoints
screens: {
'xs': '480px',
'3xl': '1920px',
},
// Custom border radius
borderRadius: {
'4xl': '2rem',
},
// Custom animations
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(20px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
plugins: [],
}
Arbitrary Values
When you need a specific value not in the scale, use []:
<!-- Any CSS value -->
<div class="w-[327px]">exactly 327px</div>
<div class="mt-[42px]">margin-top: 42px</div>
<div class="bg-[#1a1a2e]">exact hex color</div>
<div class="text-[clamp(1rem,2.5vw,2rem)]">fluid typography</div>
<div class="grid-cols-[1fr_2fr_1fr]">custom grid columns</div>
<div class="top-[calc(100vh-80px)]">calc expression</div>
@apply Directive
Extract repeated utility patterns into CSS classes:
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn {
@apply px-4 py-2 rounded font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@apply btn bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500;
}
.btn-secondary {
@apply btn bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-400;
}
.input {
@apply w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500;
}
.card {
@apply bg-white rounded-xl shadow-md overflow-hidden;
}
}
Use in HTML:
<button class="btn-primary">Save</button>
<button class="btn-secondary">Cancel</button>
<input class="input" placeholder="Enter email..." />
<div class="card p-6">...</div>
Plugins
Official Plugins
npm install @tailwindcss/typography @tailwindcss/forms @tailwindcss/aspect-ratio
// tailwind.config.js
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio'),
],
@tailwindcss/typography— beautiful prose styles for markdown content (.proseclass)@tailwindcss/forms— better default form styles@tailwindcss/aspect-ratio—aspect-w-16 aspect-h-9for responsive video
Project 1: Landing Page Hero Section
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SaaS Landing Page</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-white">
<!-- Navigation -->
<nav class="flex items-center justify-between px-6 py-4 max-w-7xl mx-auto">
<div class="text-xl font-bold text-blue-600">Acme</div>
<ul class="hidden md:flex items-center gap-8 text-gray-600 text-sm">
<li><a href="#" class="hover:text-gray-900">Features</a></li>
<li><a href="#" class="hover:text-gray-900">Pricing</a></li>
<li><a href="#" class="hover:text-gray-900">Docs</a></li>
</ul>
<div class="flex items-center gap-3">
<a href="#" class="hidden sm:block text-sm text-gray-600 hover:text-gray-900">Log in</a>
<a href="#" class="bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors">
Get started
</a>
</div>
</nav>
<!-- Hero -->
<section class="text-center px-6 pt-16 pb-24 max-w-4xl mx-auto">
<div class="inline-flex items-center gap-2 bg-blue-50 text-blue-700 text-sm px-3 py-1 rounded-full mb-6">
<span class="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></span>
Now in public beta
</div>
<h1 class="text-5xl md:text-7xl font-bold text-gray-900 leading-tight mb-6">
Build faster.<br>
<span class="text-blue-600">Ship smarter.</span>
</h1>
<p class="text-xl text-gray-500 max-w-2xl mx-auto mb-10">
The all-in-one platform for modern development teams.
Code, deploy, and scale — all in one place.
</p>
<div class="flex flex-col sm:flex-row items-center justify-center gap-4">
<a href="#" class="w-full sm:w-auto bg-blue-600 hover:bg-blue-700 text-white font-medium px-8 py-3 rounded-xl transition-colors">
Start for free
</a>
<a href="#" class="w-full sm:w-auto flex items-center justify-center gap-2 border border-gray-200 hover:border-gray-300 text-gray-700 font-medium px-8 py-3 rounded-xl transition-colors">
Watch demo
</a>
</div>
</section>
<!-- Features Grid -->
<section class="max-w-7xl mx-auto px-6 pb-24">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="p-8 rounded-2xl border border-gray-100 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
</div>
<h3 class="text-lg font-semibold text-gray-900 mb-2">Lightning Fast</h3>
<p class="text-gray-500 text-sm leading-relaxed">Deploy in seconds with our global edge network. Sub-100ms response times worldwide.</p>
</div>
<div class="p-8 rounded-2xl border border-gray-100 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
</svg>
</div>
<h3 class="text-lg font-semibold text-gray-900 mb-2">Secure by Default</h3>
<p class="text-gray-500 text-sm leading-relaxed">SOC 2 compliant. End-to-end encryption. Automatic security updates.</p>
</div>
<div class="p-8 rounded-2xl border border-gray-100 hover:shadow-lg transition-shadow">
<div class="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/>
</svg>
</div>
<h3 class="text-lg font-semibold text-gray-900 mb-2">Scale Effortlessly</h3>
<p class="text-gray-500 text-sm leading-relaxed">Auto-scales from zero to millions of users. Pay only for what you use.</p>
</div>
</div>
</section>
</body>
</html>
Project 2: React Dashboard Card Component
// src/components/StatsCard.jsx
export function StatsCard({ title, value, change, icon, color = 'blue' }) {
const colors = {
blue: 'bg-blue-50 text-blue-600',
green: 'bg-green-50 text-green-600',
purple: 'bg-purple-50 text-purple-600',
red: 'bg-red-50 text-red-600',
}
const isPositive = change >= 0
return (
<div className="bg-white rounded-xl border border-gray-100 shadow-sm p-6 hover:shadow-md transition-shadow">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-gray-500">{title}</p>
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${colors[color]}`}>
{icon}
</div>
</div>
<p className="text-3xl font-bold text-gray-900 mb-2">{value}</p>
<div className="flex items-center gap-1">
<span className={`text-sm font-medium ${isPositive ? 'text-green-600' : 'text-red-600'}`}>
{isPositive ? '↑' : '↓'} {Math.abs(change)}%
</span>
<span className="text-sm text-gray-500">vs last month</span>
</div>
</div>
)
}
// src/App.jsx
import { StatsCard } from './components/StatsCard'
export default function Dashboard() {
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="bg-white border-b border-gray-200">
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
<h1 className="text-xl font-bold text-gray-900">Dashboard</h1>
<div className="flex items-center gap-3">
<button className="text-gray-500 hover:text-gray-700 p-2 rounded-lg hover:bg-gray-100">
🔔
</button>
<div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white text-sm font-medium">
JD
</div>
</div>
</div>
</header>
<main className="max-w-7xl mx-auto px-6 py-8">
{/* Welcome */}
<div className="mb-8">
<h2 className="text-2xl font-bold text-gray-900">Good morning, John! 👋</h2>
<p className="text-gray-500 mt-1">Here's what's happening with your projects today.</p>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<StatsCard title="Total Revenue" value="$45,231" change={20.1} color="blue" icon="💰" />
<StatsCard title="Active Users" value="2,350" change={15.3} color="green" icon="👥" />
<StatsCard title="New Orders" value="1,247" change={-3.2} color="purple" icon="📦" />
<StatsCard title="Bounce Rate" value="24.3%" change={-8.1} color="red" icon="📉" />
</div>
{/* Recent Activity */}
<div className="bg-white rounded-xl border border-gray-100 shadow-sm">
<div className="p-6 border-b border-gray-100">
<h3 className="font-semibold text-gray-900">Recent Activity</h3>
</div>
<ul className="divide-y divide-gray-50">
{['New user signup: alice@example.com', 'Order #1234 completed', 'Payment received: $299', 'Server health check passed'].map((item, i) => (
<li key={i} className="flex items-center gap-4 px-6 py-4 hover:bg-gray-50 transition-colors">
<div className="w-2 h-2 rounded-full bg-blue-500 flex-shrink-0"></div>
<p className="text-sm text-gray-700 flex-1">{item}</p>
<span className="text-xs text-gray-400">{i + 1}m ago</span>
</li>
))}
</ul>
</div>
</main>
</div>
)
}
Project 3: Responsive Pricing Page
// src/components/Pricing.jsx
const plans = [
{
name: 'Starter',
price: 0,
description: 'Perfect for side projects',
features: ['3 projects', '10GB storage', 'Community support', 'Basic analytics'],
cta: 'Get started',
highlighted: false,
},
{
name: 'Pro',
price: 29,
description: 'For growing teams',
features: ['Unlimited projects', '100GB storage', 'Priority support', 'Advanced analytics', 'Custom domains', 'Team collaboration'],
cta: 'Start free trial',
highlighted: true,
},
{
name: 'Enterprise',
price: 99,
description: 'For large organizations',
features: ['Unlimited everything', '1TB storage', '24/7 dedicated support', 'Custom analytics', 'SSO + SAML', 'SLA guarantee', 'Custom contracts'],
cta: 'Contact sales',
highlighted: false,
},
]
export default function Pricing() {
return (
<section className="bg-gray-50 py-24 px-6">
<div className="max-w-5xl mx-auto">
{/* Header */}
<div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">Simple, transparent pricing</h2>
<p className="text-xl text-gray-500">No hidden fees. Cancel anytime.</p>
</div>
{/* Plans */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{plans.map((plan) => (
<div
key={plan.name}
className={`relative rounded-2xl p-8 flex flex-col ${
plan.highlighted
? 'bg-blue-600 text-white shadow-2xl scale-105'
: 'bg-white border border-gray-200 shadow-sm'
}`}
>
{plan.highlighted && (
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
<span className="bg-yellow-400 text-yellow-900 text-xs font-bold px-3 py-1 rounded-full uppercase tracking-wide">
Most Popular
</span>
</div>
)}
<div className="mb-6">
<h3 className={`text-lg font-semibold mb-1 ${plan.highlighted ? 'text-white' : 'text-gray-900'}`}>
{plan.name}
</h3>
<p className={`text-sm ${plan.highlighted ? 'text-blue-100' : 'text-gray-500'}`}>
{plan.description}
</p>
</div>
<div className="mb-8">
<span className={`text-5xl font-bold ${plan.highlighted ? 'text-white' : 'text-gray-900'}`}>
${plan.price}
</span>
<span className={`text-sm ml-1 ${plan.highlighted ? 'text-blue-200' : 'text-gray-500'}`}>
/month
</span>
</div>
<ul className="space-y-3 mb-8 flex-1">
{plan.features.map((feature) => (
<li key={feature} className="flex items-center gap-3 text-sm">
<svg className={`w-4 h-4 flex-shrink-0 ${plan.highlighted ? 'text-blue-200' : 'text-green-500'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
<span className={plan.highlighted ? 'text-blue-100' : 'text-gray-600'}>{feature}</span>
</li>
))}
</ul>
<button className={`w-full py-3 rounded-xl font-medium transition-colors ${
plan.highlighted
? 'bg-white text-blue-600 hover:bg-blue-50'
: 'bg-blue-600 text-white hover:bg-blue-700'
}`}>
{plan.cta}
</button>
</div>
))}
</div>
</div>
</section>
)
}
Common Tailwind Patterns
Card
<div class="bg-white rounded-xl shadow-md overflow-hidden max-w-sm">
<img class="w-full h-48 object-cover" src="..." alt="..." />
<div class="p-6">
<span class="text-xs font-semibold text-blue-600 uppercase tracking-wide">Category</span>
<h3 class="text-lg font-bold text-gray-900 mt-1 mb-2">Card Title</h3>
<p class="text-gray-500 text-sm leading-relaxed">Card description goes here...</p>
<button class="mt-4 text-blue-600 text-sm font-medium hover:text-blue-700">
Read more →
</button>
</div>
</div>
Badge
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Active
</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
Inactive
</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
Pending
</span>
Avatar
<!-- Single avatar -->
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-medium">
JD
</div>
<!-- Avatar with image -->
<img class="w-10 h-10 rounded-full object-cover" src="..." alt="John Doe" />
<!-- Avatar group (overlapping) -->
<div class="flex -space-x-2">
<img class="w-8 h-8 rounded-full ring-2 ring-white" src="..." alt="..." />
<img class="w-8 h-8 rounded-full ring-2 ring-white" src="..." alt="..." />
<img class="w-8 h-8 rounded-full ring-2 ring-white" src="..." alt="..." />
<div class="w-8 h-8 rounded-full ring-2 ring-white bg-gray-200 flex items-center justify-center text-xs text-gray-600 font-medium">
+5
</div>
</div>
Alert / Toast
<!-- Success -->
<div class="flex items-center gap-3 bg-green-50 border border-green-200 text-green-800 rounded-lg px-4 py-3">
<svg class="w-5 h-5 text-green-500 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<p class="text-sm font-medium">Operation completed successfully.</p>
</div>
<!-- Error -->
<div class="flex items-center gap-3 bg-red-50 border border-red-200 text-red-800 rounded-lg px-4 py-3">
<svg class="w-5 h-5 text-red-500 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<p class="text-sm font-medium">Something went wrong. Please try again.</p>
</div>
Loading Skeleton
<div class="animate-pulse space-y-4">
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
<div class="h-4 bg-gray-200 rounded w-5/6"></div>
</div>
Modal
<!-- Backdrop + Modal -->
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
<!-- Backdrop -->
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm"></div>
<!-- Modal -->
<div class="relative bg-white rounded-2xl shadow-xl w-full max-w-md p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">Confirm Action</h3>
<button class="text-gray-400 hover:text-gray-600 p-1 rounded">✕</button>
</div>
<p class="text-gray-500 text-sm mb-6">Are you sure you want to delete this item?</p>
<div class="flex justify-end gap-3">
<button class="px-4 py-2 text-sm text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-50">Cancel</button>
<button class="px-4 py-2 text-sm text-white bg-red-600 rounded-lg hover:bg-red-700">Delete</button>
</div>
</div>
</div>
Tailwind vs Related Terms
| Term | What it is |
|---|---|
| Tailwind CSS | Utility-first CSS framework |
| Bootstrap | Component-first CSS framework with pre-built UI |
| Tailwind UI | Paid component library built with Tailwind (by Tailwind Labs) |
| shadcn/ui | Free React component library using Tailwind + Radix UI |
| Headless UI | Unstyled accessible components by Tailwind Labs |
| DaisyUI | Free Tailwind component plugin |
| PostCSS | CSS transformation tool Tailwind runs on |
| PurgeCSS | Tree-shaking for CSS (built into Tailwind v3+) |
| CSS Modules | Scoped CSS per component (different approach) |
| CSS-in-JS | Styled-components, Emotion — different approach |
| Sass/SCSS | CSS preprocessor — different approach |
| UnoCSS | Atomic CSS engine, Tailwind alternative |
Common Mistakes
| Mistake | Fix |
|---|---|
| Using CDN in production | Use CLI or PostCSS build — CDN loads 3MB+ of CSS |
Forgetting content paths in config |
List all files that use Tailwind — unused classes get purged |
Using @apply excessively |
Prefer utility classes in HTML; @apply for truly shared patterns only |
Not using group and peer |
Powerful for parent/sibling state changes |
| Adding arbitrary values for standard values | Check the scale first — w-[16px] should be w-4 |
Missing dark: on all elements |
Test dark mode thoroughly — easy to miss nested elements |
flex without specifying direction |
Default is flex-row, but be explicit with flex-col for vertical |
| Not configuring custom fonts | Add fontFamily in config to avoid fallback fonts |
Learning Path
| Stage | What to learn | Time |
|---|---|---|
| 1. Setup | CDN, CLI, Vite setup | 1 day |
| 2. Core utilities | Spacing, typography, colors, sizing | 3 days |
| 3. Layout | Flex, Grid, responsive design | 1 week |
| 4. Interactive | Hover, focus, transitions, dark mode | 3 days |
| 5. Customization | tailwind.config.js, @apply, plugins | 3 days |
| 6. Real projects | Build landing pages, dashboards | 2 weeks |
| 7. Ecosystem | shadcn/ui, Headless UI, DaisyUI | 1 week |
Free Resources
| Resource | URL |
|---|---|
| Official Docs | tailwindcss.com/docs |
| Tailwind Play | play.tailwindcss.com (online playground) |
| shadcn/ui | ui.shadcn.com |
| Headless UI | headlessui.com |
| DaisyUI | daisyui.com |
| Flowbite | flowbite.com (free Tailwind components) |
| Tailwind Cheat Sheet | nerdcave.com/tailwind-cheat-sheet |
FAQ
Q: Should I use Tailwind or Bootstrap?
Tailwind if you want unique designs without fighting overrides. Bootstrap if you need a quick prototype and don't care about the "Bootstrap look". Tailwind has become far more popular for production apps in 2024–2025.
Q: Do I still write any CSS with Tailwind?
Rarely. Use tailwind.config.js for custom values and @layer components with @apply for truly repeated patterns. Most things don't need custom CSS.
Q: Isn't having hundreds of classes in HTML messy?
It can look cluttered. The tradeoff: you never need to look at a separate CSS file to understand styling. Extract complex patterns into React/Vue components. Use @apply for common utility groups.
Q: Does Tailwind work with TypeScript?
Yes. The config file has TypeScript support (tailwind.config.ts). Works perfectly with typed React/Vue/Angular projects.
Q: What's the difference between Tailwind CSS and Tailwind UI?
Tailwind CSS is free and open-source. Tailwind UI is a paid ($299 one-time) component library built with Tailwind by the same team. shadcn/ui is a free alternative.
Q: How do I add a custom font?
- Import in
index.htmlorindex.css(Google Fonts or local). 2. Add totailwind.config.jsundertheme.extend.fontFamily. 3. Apply withfont-sansor your custom key.
Quick Reference
<!-- Layout -->
flex flex-col flex-row items-center justify-between gap-4
grid grid-cols-3 col-span-2
<!-- Spacing -->
p-4 px-6 py-3 mt-8 mb-4 mx-auto space-y-4
<!-- Typography -->
text-xl font-bold text-gray-900 leading-tight tracking-wide uppercase
<!-- Colors -->
bg-white bg-blue-600 text-white text-gray-500 border-gray-200
<!-- Sizing -->
w-full h-screen max-w-7xl min-h-screen
<!-- Responsive -->
sm:flex md:hidden lg:grid-cols-3 xl:text-xl
<!-- State -->
hover:bg-blue-700 focus:ring-2 active:scale-95 disabled:opacity-50
<!-- Dark mode -->
dark:bg-gray-900 dark:text-white dark:border-gray-700
<!-- Animation -->
transition-colors duration-200 animate-pulse animate-spin