Tailwind CSS - Kompletny przewodnik po frameworku CSS, który zmienił front-end development
Czym jest Tailwind CSS i dlaczego zdominował rynek?
Tailwind CSS to utility-first framework CSS, który zmienił sposób w jaki myślimy o stylowaniu aplikacji webowych. Zamiast pisać własne klasy CSS i zastanawiać się nad nazwami (card-wrapper, button-primary), używasz gotowych klas utility bezpośrednio w HTML/JSX.
W 2025 roku Tailwind jest używany przez większość nowoczesnych aplikacji webowych i stał się de facto standardem w ekosystemie React, Vue i innych frameworków.
Dlaczego utility-first zmienia wszystko?
Tradycyjne podejście (CSS/SCSS)
/* styles.css */
.card {
background-color: white;
border-radius: 8px;
padding: 24px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.card-title {
font-size: 20px;
font-weight: bold;
color: #1a202c;
margin-bottom: 8px;
}
.card-description {
color: #718096;
font-size: 14px;
}<div class="card">
<h2 class="card-title">Tytuł</h2>
<p class="card-description">Opis karty...</p>
</div>Podejście Tailwind
<div class="bg-white rounded-lg p-6 shadow-md">
<h2 class="text-xl font-bold text-gray-900 mb-2">Tytuł</h2>
<p class="text-sm text-gray-600">Opis karty...</p>
</div>Korzyści:
- Nie wymyślasz nazw klas
- Style są lokalne dla komponentu
- Natychmiast widzisz co element robi
- Zero CSS do pisania
- Mniejszy bundle (tylko użyte klasy)
Instalacja i konfiguracja
Next.js (automatyczna konfiguracja)
npx create-next-app@latest my-app
# Wybierz "Yes" dla Tailwind CSSRęczna instalacja
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
}/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;Podstawy Tailwind CSS
Spacing (margin, padding)
<!-- Margin -->
<div class="m-4">margin: 16px (wszystkie strony)</div>
<div class="mx-4">margin-left/right: 16px</div>
<div class="my-4">margin-top/bottom: 16px</div>
<div class="mt-4">margin-top: 16px</div>
<div class="ml-auto">margin-left: auto</div>
<!-- Padding -->
<div class="p-4">padding: 16px</div>
<div class="px-6 py-3">padding-x: 24px, padding-y: 12px</div>
<!-- Wartości: 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96 -->
<!-- Jeden unit = 4px, więc p-4 = 16px -->Flexbox i Grid
<!-- Flexbox -->
<div class="flex items-center justify-between gap-4">
<span>Left</span>
<span>Right</span>
</div>
<div class="flex flex-col md:flex-row gap-6">
<div class="flex-1">Column 1</div>
<div class="flex-1">Column 2</div>
</div>
<!-- Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<div class="grid grid-cols-12 gap-4">
<div class="col-span-8">Main content (8/12)</div>
<div class="col-span-4">Sidebar (4/12)</div>
</div>Typography
<!-- Rozmiary tekstu -->
<p class="text-xs">Extra small (12px)</p>
<p class="text-sm">Small (14px)</p>
<p class="text-base">Base (16px)</p>
<p class="text-lg">Large (18px)</p>
<p class="text-xl">Extra large (20px)</p>
<p class="text-2xl">2XL (24px)</p>
<p class="text-3xl">3XL (30px)</p>
<p class="text-4xl">4XL (36px)</p>
<!-- Font weight -->
<p class="font-thin">Thin (100)</p>
<p class="font-light">Light (300)</p>
<p class="font-normal">Normal (400)</p>
<p class="font-medium">Medium (500)</p>
<p class="font-semibold">Semibold (600)</p>
<p class="font-bold">Bold (700)</p>
<!-- Line height -->
<p class="leading-tight">Tight (1.25)</p>
<p class="leading-normal">Normal (1.5)</p>
<p class="leading-relaxed">Relaxed (1.625)</p>
<!-- Text alignment -->
<p class="text-left">Left aligned</p>
<p class="text-center">Center aligned</p>
<p class="text-right">Right aligned</p>Kolory
Tailwind ma system kolorów z odcieniami 50-950:
<!-- Tekst -->
<p class="text-gray-500">Szary tekst</p>
<p class="text-blue-600">Niebieski tekst</p>
<p class="text-red-500">Czerwony tekst</p>
<!-- Tło -->
<div class="bg-white">Białe tło</div>
<div class="bg-gray-100">Jasno szare tło</div>
<div class="bg-blue-500">Niebieskie tło</div>
<!-- Border -->
<div class="border border-gray-200">Szara ramka</div>
<div class="border-2 border-blue-500">Gruba niebieska ramka</div>
<!-- Dostępne kolory: slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose -->Responsywność
Tailwind używa mobile-first approach z breakpoints:
<!-- Breakpoints: sm(640px), md(768px), lg(1024px), xl(1280px), 2xl(1536px) -->
<div class="w-full md:w-1/2 lg:w-1/3">
<!-- 100% na mobile, 50% od medium, 33% od large -->
</div>
<div class="flex flex-col md:flex-row">
<!-- Stack na mobile, row od medium -->
</div>
<div class="hidden lg:block">
<!-- Ukryty na mobile i tablet, widoczny od large -->
</div>
<div class="text-sm md:text-base lg:text-lg">
<!-- Różne rozmiary tekstu dla różnych ekranów -->
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- 1 kolumna mobile, 2 tablet, 4 desktop -->
</div>Dark Mode
<!-- Automatyczny dark mode (system preference) -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Automatyczne przełączanie
</div>
<!-- Pełna karta z dark mode -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg dark:shadow-gray-900/20 p-6">
<h2 class="text-gray-900 dark:text-white font-bold">Tytuł</h2>
<p class="text-gray-600 dark:text-gray-300">Opis...</p>
<button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white px-4 py-2 rounded">
Kliknij
</button>
</div>// tailwind.config.js - włącz class-based dark mode
module.exports = {
darkMode: 'class', // zamiast 'media'
// ...
}// Toggle dark mode
function toggleDarkMode() {
document.documentElement.classList.toggle('dark')
}States (hover, focus, active)
<!-- Hover -->
<button class="bg-blue-500 hover:bg-blue-600 transition-colors">
Hover me
</button>
<!-- Focus -->
<input class="border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 outline-none">
<!-- Active -->
<button class="bg-blue-500 active:bg-blue-700">
Click me
</button>
<!-- Group hover -->
<div class="group cursor-pointer">
<h2 class="group-hover:text-blue-500">Tytuł</h2>
<p class="group-hover:underline">Opis...</p>
</div>
<!-- Peer (sibling) -->
<input class="peer" type="checkbox" />
<label class="peer-checked:text-blue-500">Label</label>
<!-- Disabled -->
<button class="bg-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed" disabled>
Disabled
</button>Zaawansowane techniki
Animacje
<!-- Wbudowane animacje -->
<div class="animate-spin">Spinner</div>
<div class="animate-ping">Ping effect</div>
<div class="animate-pulse">Skeleton loader</div>
<div class="animate-bounce">Bounce</div>
<!-- Transition -->
<button class="transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-lg">
Hover me
</button>
<!-- Transform -->
<div class="hover:-translate-y-1 hover:rotate-3 transition-transform">
Card
</div>Custom animations w config
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-out',
'slide-up': 'slideUp 0.3s ease-out',
'scale-in': 'scaleIn 0.2s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
scaleIn: {
'0%': { transform: 'scale(0.95)', opacity: '0' },
'100%': { transform: 'scale(1)', opacity: '1' },
},
},
},
},
}<div class="animate-fade-in">Fading in...</div>
<div class="animate-slide-up">Sliding up...</div>Gradienty
<!-- Linear gradient -->
<div class="bg-gradient-to-r from-blue-500 to-purple-500">
Left to right gradient
</div>
<div class="bg-gradient-to-br from-pink-500 via-red-500 to-yellow-500">
Diagonal gradient with middle color
</div>
<!-- Text gradient -->
<h1 class="text-4xl font-bold bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
Gradient Text
</h1>Typography Plugin
npm install @tailwindcss/typography// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/typography')],
}<!-- Automatyczne stylowanie prose -->
<article class="prose prose-lg dark:prose-invert max-w-none">
<h1>Artykuł</h1>
<p>Lorem ipsum dolor sit amet...</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<blockquote>Cytat...</blockquote>
<pre><code>const x = 1;</code></pre>
</article>Forms Plugin
npm install @tailwindcss/forms// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/forms')],
}<!-- Automatyczne stylowanie formularzy -->
<input type="text" class="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500" />
<select class="rounded-lg border-gray-300">
<option>Option 1</option>
</select>
<input type="checkbox" class="rounded text-blue-500 focus:ring-blue-500" />Container Queries
npm install @tailwindcss/container-queries<div class="@container">
<div class="@md:flex @md:gap-4">
<!-- Responsive based on container, not viewport -->
</div>
</div>Tailwind z React/Next.js
Komponent Button
// components/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-lg font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-blue-500 text-white hover:bg-blue-600 focus-visible:ring-blue-500',
destructive: 'bg-red-500 text-white hover:bg-red-600 focus-visible:ring-red-500',
outline: 'border border-gray-300 bg-white hover:bg-gray-50 focus-visible:ring-gray-500',
ghost: 'hover:bg-gray-100 focus-visible:ring-gray-500',
link: 'text-blue-500 underline-offset-4 hover:underline',
},
size: {
sm: 'h-8 px-3 text-sm',
default: 'h-10 px-4',
lg: 'h-12 px-6 text-lg',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
export function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}// Użycie
<Button>Default</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline" size="lg">Large Outline</Button>Helper cn()
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}// Użycie
import { cn } from '@/lib/utils'
function Card({ className, ...props }) {
return (
<div
className={cn(
'rounded-lg border bg-white p-6 shadow-sm',
className // Możliwość nadpisania klas
)}
{...props}
/>
)
}
// Nadpisanie domyślnych klas
<Card className="bg-gray-100 p-8" />Kondycyjne klasy
function Alert({ type, children }) {
return (
<div
className={cn(
'rounded-lg p-4 border',
{
'bg-green-50 border-green-200 text-green-800': type === 'success',
'bg-red-50 border-red-200 text-red-800': type === 'error',
'bg-yellow-50 border-yellow-200 text-yellow-800': type === 'warning',
'bg-blue-50 border-blue-200 text-blue-800': type === 'info',
}
)}
>
{children}
</div>
)
}Konfiguracja zaawansowana
Custom colors
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Brand colors
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9', // Primary
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
// Semantic colors
success: '#22c55e',
warning: '#f59e0b',
error: '#ef4444',
},
},
},
}Custom spacing
module.exports = {
theme: {
extend: {
spacing: {
'18': '4.5rem', // 72px
'88': '22rem', // 352px
'128': '32rem', // 512px
},
},
},
}Custom breakpoints
module.exports = {
theme: {
screens: {
'xs': '475px',
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
'3xl': '1920px',
},
},
}Custom fonts
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
display: ['Cal Sans', 'Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
},
},
}Praktyczne komponenty
Card
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow">
<img src="/image.jpg" alt="" class="w-full h-48 object-cover" />
<div class="p-6">
<span class="text-sm text-blue-500 font-medium">Category</span>
<h3 class="text-xl font-bold text-gray-900 dark:text-white mt-2">
Card Title
</h3>
<p class="text-gray-600 dark:text-gray-300 mt-2">
Card description goes here...
</p>
<div class="mt-4 flex items-center justify-between">
<span class="text-2xl font-bold text-gray-900 dark:text-white">$99</span>
<button class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
Buy Now
</button>
</div>
</div>
</div>Navigation
<nav class="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<div class="flex-shrink-0">
<span class="text-xl font-bold text-gray-900 dark:text-white">Logo</span>
</div>
<!-- Desktop menu -->
<div class="hidden md:flex items-center gap-8">
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors">
Home
</a>
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors">
Products
</a>
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors">
About
</a>
</div>
<!-- CTA -->
<div class="flex items-center gap-4">
<button class="hidden sm:block px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">
Sign In
</button>
<button class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
Get Started
</button>
</div>
</div>
</div>
</nav>Form
<form class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Email
</label>
<input
type="email"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-shadow"
placeholder="you@example.com"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Password
</label>
<input
type="password"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-shadow"
placeholder="••••••••"
/>
</div>
<div class="flex items-center justify-between">
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox" class="w-4 h-4 text-blue-500 rounded focus:ring-blue-500" />
<span class="text-sm text-gray-600 dark:text-gray-400">Remember me</span>
</label>
<a href="#" class="text-sm text-blue-500 hover:underline">
Forgot password?
</a>
</div>
<button
type="submit"
class="w-full py-3 bg-blue-500 text-white rounded-lg font-medium hover:bg-blue-600 focus:ring-4 focus:ring-blue-500/20 transition-all"
>
Sign In
</button>
</form>Tailwind vs Alternatywy
| Aspekt | Tailwind CSS | Styled Components | CSS Modules |
|---|---|---|---|
| Podejście | Utility classes | CSS-in-JS | Scoped CSS |
| Bundle size | Małe (tree shaking) | Średnie | Małe |
| Runtime | Brak | Tak | Brak |
| DX | Świetne z IntelliSense | Dobre | Dobre |
| Learning curve | Średnia | Niska | Niska |
| Customization | Pełna | Pełna | Pełna |
Best Practices
Do's
✅ Używaj składni mobile-first (domyślne → sm → md → lg)
✅ Grupuj powiązane klasy (layout, spacing, colors, states)
✅ Twórz komponenty dla powtarzających się wzorców
✅ Używaj cn() dla warunkowych klas
✅ Konfiguruj design system w tailwind.config.js
✅ Używaj @apply tylko dla bardzo powtarzalnych wzorcówDon'ts
❌ Nie nadużywaj @apply - traci się zalety utility-first
❌ Nie pisz inline styles - użyj arbitrary values [color:#123456]
❌ Nie duplikuj klas - stwórz komponent
❌ Nie ignoruj responsive design
❌ Nie używaj !important (arbitrary: [!important])Narzędzia i rozszerzenia
VS Code IntelliSense
Zainstaluj oficjalne rozszerzenie "Tailwind CSS IntelliSense" - autouzupełnianie, hover preview, linting.
Prettier Plugin
npm install -D prettier prettier-plugin-tailwindcssAutomatycznie sortuje klasy Tailwind w rekomendowanej kolejności.
Tailwind UI
Oficjalna biblioteka komponentów od twórców Tailwind - płatna ale warta każdej złotówki dla profesjonalnych projektów.
FAQ
Czy Tailwind zwiększa rozmiar HTML?
Tak, HTML jest większy, ale CSS jest znacznie mniejszy. Tailwind automatycznie usuwa nieużywane klasy w produkcji, więc finalny bundle jest często mniejszy niż tradycyjne podejście.
Czy muszę pamiętać wszystkie klasy?
Nie. Z IntelliSense masz autouzupełnianie i podgląd. Po kilku dniach zapamiętasz najczęściej używane klasy naturalnie.
Jak używać z istniejącym CSS?
Tailwind współistnieje z tradycyjnym CSS. Możesz stopniowo migrować lub używać obu podejść jednocześnie.
Czy Tailwind jest dobry dla dużych projektów?
Tak, szczególnie z dobrą architekturą komponentów. Firmy jak GitHub, Netflix, Shopify używają Tailwind w produkcji.
Podsumowanie
Tailwind CSS zmienił sposób w jaki stylujemy aplikacje webowe. Utility-first podejście początkowo wydaje się dziwne, ale po przyzwyczajeniu się znacząco przyspiesza development i ułatwia utrzymanie kodu.
Kluczowe zalety:
- Szybsze pisanie stylów - bez wymyślania nazw klas
- Mniejszy bundle - tylko użyte klasy
- Spójny design system - wbudowany system spacing, colors, typography
- Świetne DX - IntelliSense, hot reload, dokumentacja
Tailwind CSS - Complete Guide to the Utility-First CSS Framework That Changed Frontend Development
What is Tailwind CSS and Why Did It Dominate the Market?
Tailwind CSS is a utility-first CSS framework that changed the way we think about styling web applications. Instead of writing your own CSS classes and figuring out names (card-wrapper, button-primary), you use ready-made utility classes directly in your HTML/JSX.
In 2025, Tailwind is used by most modern web applications and has become the de facto standard in the React, Vue, and other framework ecosystems.
Why Utility-First Changes Everything
Traditional Approach (CSS/SCSS)
/* styles.css */
.card {
background-color: white;
border-radius: 8px;
padding: 24px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.card-title {
font-size: 20px;
font-weight: bold;
color: #1a202c;
margin-bottom: 8px;
}
.card-description {
color: #718096;
font-size: 14px;
}<div class="card">
<h2 class="card-title">Title</h2>
<p class="card-description">Card description...</p>
</div>Tailwind Approach
<div class="bg-white rounded-lg p-6 shadow-md">
<h2 class="text-xl font-bold text-gray-900 mb-2">Title</h2>
<p class="text-sm text-gray-600">Card description...</p>
</div>Benefits:
- No inventing class names
- Styles are local to the component
- You immediately see what the element does
- Zero CSS to write
- Smaller bundle (only used classes)
Installation and Configuration
Next.js (Automatic Configuration)
npx create-next-app@latest my-app
# Select "Yes" for Tailwind CSSManual Installation
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
}/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;Tailwind CSS Basics
Spacing (margin, padding)
<!-- Margin -->
<div class="m-4">margin: 16px (all sides)</div>
<div class="mx-4">margin-left/right: 16px</div>
<div class="my-4">margin-top/bottom: 16px</div>
<div class="mt-4">margin-top: 16px</div>
<div class="ml-auto">margin-left: auto</div>
<!-- Padding -->
<div class="p-4">padding: 16px</div>
<div class="px-6 py-3">padding-x: 24px, padding-y: 12px</div>
<!-- Values: 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96 -->
<!-- One unit = 4px, so p-4 = 16px -->Flexbox and Grid
<!-- Flexbox -->
<div class="flex items-center justify-between gap-4">
<span>Left</span>
<span>Right</span>
</div>
<div class="flex flex-col md:flex-row gap-6">
<div class="flex-1">Column 1</div>
<div class="flex-1">Column 2</div>
</div>
<!-- Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<div class="grid grid-cols-12 gap-4">
<div class="col-span-8">Main content (8/12)</div>
<div class="col-span-4">Sidebar (4/12)</div>
</div>Typography
<!-- Text sizes -->
<p class="text-xs">Extra small (12px)</p>
<p class="text-sm">Small (14px)</p>
<p class="text-base">Base (16px)</p>
<p class="text-lg">Large (18px)</p>
<p class="text-xl">Extra large (20px)</p>
<p class="text-2xl">2XL (24px)</p>
<p class="text-3xl">3XL (30px)</p>
<p class="text-4xl">4XL (36px)</p>
<!-- Font weight -->
<p class="font-thin">Thin (100)</p>
<p class="font-light">Light (300)</p>
<p class="font-normal">Normal (400)</p>
<p class="font-medium">Medium (500)</p>
<p class="font-semibold">Semibold (600)</p>
<p class="font-bold">Bold (700)</p>
<!-- Line height -->
<p class="leading-tight">Tight (1.25)</p>
<p class="leading-normal">Normal (1.5)</p>
<p class="leading-relaxed">Relaxed (1.625)</p>
<!-- Text alignment -->
<p class="text-left">Left aligned</p>
<p class="text-center">Center aligned</p>
<p class="text-right">Right aligned</p>Colors
Tailwind has a color system with shades 50-950:
<!-- Text -->
<p class="text-gray-500">Gray text</p>
<p class="text-blue-600">Blue text</p>
<p class="text-red-500">Red text</p>
<!-- Background -->
<div class="bg-white">White background</div>
<div class="bg-gray-100">Light gray background</div>
<div class="bg-blue-500">Blue background</div>
<!-- Border -->
<div class="border border-gray-200">Gray border</div>
<div class="border-2 border-blue-500">Thick blue border</div>
<!-- Available colors: slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose -->Responsiveness
Tailwind uses a mobile-first approach with breakpoints:
<!-- Breakpoints: sm(640px), md(768px), lg(1024px), xl(1280px), 2xl(1536px) -->
<div class="w-full md:w-1/2 lg:w-1/3">
<!-- 100% on mobile, 50% from medium, 33% from large -->
</div>
<div class="flex flex-col md:flex-row">
<!-- Stack on mobile, row from medium -->
</div>
<div class="hidden lg:block">
<!-- Hidden on mobile and tablet, visible from large -->
</div>
<div class="text-sm md:text-base lg:text-lg">
<!-- Different text sizes for different screens -->
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- 1 column mobile, 2 tablet, 4 desktop -->
</div>Dark Mode
<!-- Automatic dark mode (system preference) -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Automatic switching
</div>
<!-- Full card with dark mode -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg dark:shadow-gray-900/20 p-6">
<h2 class="text-gray-900 dark:text-white font-bold">Title</h2>
<p class="text-gray-600 dark:text-gray-300">Description...</p>
<button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white px-4 py-2 rounded">
Click
</button>
</div>// tailwind.config.js - enable class-based dark mode
module.exports = {
darkMode: 'class', // instead of 'media'
// ...
}// Toggle dark mode
function toggleDarkMode() {
document.documentElement.classList.toggle('dark')
}States (hover, focus, active)
<!-- Hover -->
<button class="bg-blue-500 hover:bg-blue-600 transition-colors">
Hover me
</button>
<!-- Focus -->
<input class="border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 outline-none">
<!-- Active -->
<button class="bg-blue-500 active:bg-blue-700">
Click me
</button>
<!-- Group hover -->
<div class="group cursor-pointer">
<h2 class="group-hover:text-blue-500">Title</h2>
<p class="group-hover:underline">Description...</p>
</div>
<!-- Peer (sibling) -->
<input class="peer" type="checkbox" />
<label class="peer-checked:text-blue-500">Label</label>
<!-- Disabled -->
<button class="bg-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed" disabled>
Disabled
</button>Advanced Techniques
Animations
<!-- Built-in animations -->
<div class="animate-spin">Spinner</div>
<div class="animate-ping">Ping effect</div>
<div class="animate-pulse">Skeleton loader</div>
<div class="animate-bounce">Bounce</div>
<!-- Transition -->
<button class="transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-lg">
Hover me
</button>
<!-- Transform -->
<div class="hover:-translate-y-1 hover:rotate-3 transition-transform">
Card
</div>Custom Animations in Config
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-out',
'slide-up': 'slideUp 0.3s ease-out',
'scale-in': 'scaleIn 0.2s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
scaleIn: {
'0%': { transform: 'scale(0.95)', opacity: '0' },
'100%': { transform: 'scale(1)', opacity: '1' },
},
},
},
},
}<div class="animate-fade-in">Fading in...</div>
<div class="animate-slide-up">Sliding up...</div>Gradients
<!-- Linear gradient -->
<div class="bg-gradient-to-r from-blue-500 to-purple-500">
Left to right gradient
</div>
<div class="bg-gradient-to-br from-pink-500 via-red-500 to-yellow-500">
Diagonal gradient with middle color
</div>
<!-- Text gradient -->
<h1 class="text-4xl font-bold bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
Gradient Text
</h1>Typography Plugin
npm install @tailwindcss/typography// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/typography')],
}<!-- Automatic prose styling -->
<article class="prose prose-lg dark:prose-invert max-w-none">
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet...</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<blockquote>Quote...</blockquote>
<pre><code>const x = 1;</code></pre>
</article>Forms Plugin
npm install @tailwindcss/forms// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/forms')],
}<!-- Automatic form styling -->
<input type="text" class="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500" />
<select class="rounded-lg border-gray-300">
<option>Option 1</option>
</select>
<input type="checkbox" class="rounded text-blue-500 focus:ring-blue-500" />Tailwind with React/Next.js
Button Component
// components/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-lg font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-blue-500 text-white hover:bg-blue-600 focus-visible:ring-blue-500',
destructive: 'bg-red-500 text-white hover:bg-red-600 focus-visible:ring-red-500',
outline: 'border border-gray-300 bg-white hover:bg-gray-50 focus-visible:ring-gray-500',
ghost: 'hover:bg-gray-100 focus-visible:ring-gray-500',
link: 'text-blue-500 underline-offset-4 hover:underline',
},
size: {
sm: 'h-8 px-3 text-sm',
default: 'h-10 px-4',
lg: 'h-12 px-6 text-lg',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
export function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}// Usage
<Button>Default</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline" size="lg">Large Outline</Button>Helper cn()
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}// Usage
import { cn } from '@/lib/utils'
function Card({ className, ...props }) {
return (
<div
className={cn(
'rounded-lg border bg-white p-6 shadow-sm',
className // Ability to override classes
)}
{...props}
/>
)
}
// Overriding default classes
<Card className="bg-gray-100 p-8" />Conditional Classes
function Alert({ type, children }) {
return (
<div
className={cn(
'rounded-lg p-4 border',
{
'bg-green-50 border-green-200 text-green-800': type === 'success',
'bg-red-50 border-red-200 text-red-800': type === 'error',
'bg-yellow-50 border-yellow-200 text-yellow-800': type === 'warning',
'bg-blue-50 border-blue-200 text-blue-800': type === 'info',
}
)}
>
{children}
</div>
)
}Advanced Configuration
Custom Colors
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Brand colors
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9', // Primary
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
// Semantic colors
success: '#22c55e',
warning: '#f59e0b',
error: '#ef4444',
},
},
},
}Custom Spacing
module.exports = {
theme: {
extend: {
spacing: {
'18': '4.5rem', // 72px
'88': '22rem', // 352px
'128': '32rem', // 512px
},
},
},
}Custom Breakpoints
module.exports = {
theme: {
screens: {
'xs': '475px',
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
'3xl': '1920px',
},
},
}Custom Fonts
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
display: ['Cal Sans', 'Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
},
},
}Practical Components
Card
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow">
<img src="/image.jpg" alt="" class="w-full h-48 object-cover" />
<div class="p-6">
<span class="text-sm text-blue-500 font-medium">Category</span>
<h3 class="text-xl font-bold text-gray-900 dark:text-white mt-2">
Card Title
</h3>
<p class="text-gray-600 dark:text-gray-300 mt-2">
Card description goes here...
</p>
<div class="mt-4 flex items-center justify-between">
<span class="text-2xl font-bold text-gray-900 dark:text-white">$99</span>
<button class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
Buy Now
</button>
</div>
</div>
</div>Navigation
<nav class="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<div class="flex-shrink-0">
<span class="text-xl font-bold text-gray-900 dark:text-white">Logo</span>
</div>
<!-- Desktop menu -->
<div class="hidden md:flex items-center gap-8">
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors">
Home
</a>
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors">
Products
</a>
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors">
About
</a>
</div>
<!-- CTA -->
<div class="flex items-center gap-4">
<button class="hidden sm:block px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">
Sign In
</button>
<button class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
Get Started
</button>
</div>
</div>
</div>
</nav>Form
<form class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Email
</label>
<input
type="email"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-shadow"
placeholder="you@example.com"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Password
</label>
<input
type="password"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-shadow"
placeholder="••••••••"
/>
</div>
<div class="flex items-center justify-between">
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox" class="w-4 h-4 text-blue-500 rounded focus:ring-blue-500" />
<span class="text-sm text-gray-600 dark:text-gray-400">Remember me</span>
</label>
<a href="#" class="text-sm text-blue-500 hover:underline">
Forgot password?
</a>
</div>
<button
type="submit"
class="w-full py-3 bg-blue-500 text-white rounded-lg font-medium hover:bg-blue-600 focus:ring-4 focus:ring-blue-500/20 transition-all"
>
Sign In
</button>
</form>Tailwind vs Alternatives
| Aspect | Tailwind CSS | Styled Components | CSS Modules |
|---|---|---|---|
| Approach | Utility classes | CSS-in-JS | Scoped CSS |
| Bundle size | Small (tree shaking) | Medium | Small |
| Runtime | None | Yes | None |
| DX | Great with IntelliSense | Good | Good |
| Learning curve | Medium | Low | Low |
| Customization | Full | Full | Full |
Best Practices
Do's
✅ Use mobile-first syntax (default → sm → md → lg)
✅ Group related classes (layout, spacing, colors, states)
✅ Create components for repeating patterns
✅ Use cn() for conditional classes
✅ Configure design system in tailwind.config.js
✅ Use @apply only for very repetitive patternsDon'ts
❌ Don't overuse @apply - you lose utility-first benefits
❌ Don't write inline styles - use arbitrary values [color:#123456]
❌ Don't duplicate classes - create a component
❌ Don't ignore responsive design
❌ Don't use !important (arbitrary: [!important])Tools and Extensions
VS Code IntelliSense
Install the official "Tailwind CSS IntelliSense" extension - autocomplete, hover preview, linting.
Prettier Plugin
npm install -D prettier prettier-plugin-tailwindcssAutomatically sorts Tailwind classes in the recommended order.
Tailwind UI
Official component library from Tailwind creators - paid but worth every penny for professional projects.
FAQ
Does Tailwind increase HTML size?
Yes, HTML is larger, but CSS is significantly smaller. Tailwind automatically removes unused classes in production, so the final bundle is often smaller than the traditional approach.
Do I have to memorize all classes?
No. With IntelliSense you have autocomplete and preview. After a few days, you'll naturally remember the most commonly used classes.
How do I use it with existing CSS?
Tailwind coexists with traditional CSS. You can gradually migrate or use both approaches simultaneously.
Is Tailwind good for large projects?
Yes, especially with good component architecture. Companies like GitHub, Netflix, and Shopify use Tailwind in production.
Summary
Tailwind CSS changed how we style web applications. The utility-first approach initially seems strange, but once you get used to it, it significantly speeds up development and makes code maintenance easier.
Key advantages:
- Faster style writing - no inventing class names
- Smaller bundle - only used classes
- Consistent design system - built-in spacing, colors, typography system
- Great DX - IntelliSense, hot reload, documentation