By the end of this lesson, you will:
:information_source: Tailwind CSS is a utility-first CSS framework that gives you low-level utility classes to build custom designs directly in your HTML. Instead of pre-built components like Bootstrap, Tailwind provides the building blocks to create unique designs without writing custom CSS.
Welcome to the world of utility-first CSS! After exploring why frameworks exist, let's experience Tailwind CSS firsthand. Tailwind changes how we think about styling by letting us compose utility classes to build custom designs.
Instead of writing custom CSS classes, you apply utility classes directly in your HTML:
:bulb: Tip Think of utility classes as LEGO blocks - each one does one specific thing, and you combine them to build whatever you want!
Example:
{/* Traditional CSS approach */}
<div class="hero-section">
<h1 class="hero-title">Welcome</h1>
<button class="hero-button">Get Started</button>
</div>
{/* Tailwind CSS approach */}
<div class="bg-blue-500 py-12 px-4 text-center">
<h1 class="text-4xl font-bold text-white mb-4">Welcome</h1>
<button class="bg-white text-blue-500 py-3 px-6 rounded-md hover:bg-gray-100 transition">
Get Started
</button>
</div>
Let's compare traditional CSS with Tailwind to see the difference:
/* style.css */
.hero-section {
background-color: #f3f4f6;
padding: 3rem 1rem;
text-align: center;
}
.hero-title {
font-size: 2.5rem;
font-weight: bold;
color: #1f2937;
margin-bottom: 1rem;
}
.hero-button {
background-color: #3b82f6;
color: white;
padding: 0.75rem 1.5rem;
border-radius: 0.375rem;
border: none;
cursor: pointer;
}
{/* No custom CSS needed! */}
<section class="bg-gray-100 py-12 px-4 text-center">
<h1 class="text-4xl font-bold text-gray-800 mb-4">Welcome to Tailwind</h1>
<button class="bg-blue-500 text-white py-3 px-6 rounded-md hover:bg-blue-600 transition">
Get Started
</button>
</section>
:memo: Note With Tailwind, you style elements directly in your HTML. No switching between files, no naming CSS classes, just apply utilities and see results instantly!
There are three ways to get started with Tailwind:
Perfect for learning and prototyping:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tailwind CSS Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
{/* Your content here */}
</body>
</html>
Best for real projects:
# Install Tailwind CSS
npm install -D tailwindcss
npx tailwindcss init
# Create your CSS file
echo "@tailwind base;
@tailwind components;
@tailwind utilities;" > styles/input.css
# Build your CSS
npx tailwindcss -i ./styles/input.css -o ./styles/output.css --watch
Install the "Tailwind CSS IntelliSense" extension for:
Tailwind gives you utilities for almost every CSS property. Let's explore the most common ones:
{/* Padding */}
<div class="p-4">Padding all sides</div>
<div class="pt-2 pb-4 px-6">Different padding values</div>
{/* Margin */}
<div class="m-4">Margin all sides</div>
<div class="mt-8 mb-2">Top and bottom margin</div>
<div class="mx-auto">Center horizontally</div>
:bulb: Tip The number in spacing utilities represents a scale:
p-1
= 0.25rem,p-2
= 0.5rem,p-4
= 1rem, and so on!
{/* Font Size */}
<p class="text-xs">Extra small text</p>
<p class="text-sm">Small text</p>
<p class="text-base">Base text</p>
<p class="text-lg">Large text</p>
<p class="text-xl">Extra large text</p>
<p class="text-2xl">2x large text</p>
{/* Font Weight */}
<p class="font-light">Light weight</p>
<p class="font-normal">Normal weight</p>
<p class="font-semibold">Semibold weight</p>
<p class="font-bold">Bold weight</p>
{/* Text Color */}
<p class="text-gray-500">Gray text</p>
<p class="text-blue-600">Blue text</p>
<p class="text-red-500">Red text</p>
{/* Display */}
<div class="block">Block element</div>
<div class="inline-block">Inline block</div>
<div class="hidden">Hidden element</div>
{/* Flexbox */}
<div class="flex justify-between items-center">
<div>Left</div>
<div>Right</div>
</div>
{/* Grid */}
<div class="grid grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
Tailwind makes responsive design super easy with prefixes:
:information_source: Responsive Prefixes:
sm:
-> 640px and up (small devices)md:
-> 768px and up (tablets)lg:
-> 1024px and up (laptops)xl:
-> 1280px and up (desktops)2xl:
-> 1536px and up (large screens)
Example:
{/* Responsive grid */}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="bg-white p-4 rounded shadow">Card 1</div>
<div class="bg-white p-4 rounded shadow">Card 2</div>
<div class="bg-white p-4 rounded shadow">Card 3</div>
</div>
{/* Responsive text */}
<h1 class="text-2xl md:text-4xl lg:text-6xl">
Responsive Heading
</h1>
{/* Hide/show at different breakpoints */}
<div class="block lg:hidden">Mobile menu</div>
<div class="hidden lg:block">Desktop menu</div>
:memo: Note Tailwind uses a mobile-first approach. Classes without prefixes apply to all screen sizes, then you add larger breakpoints as needed.
Tailwind lets you style different states easily:
Example:
{/* Hover */}
<button class="bg-blue-500 hover:bg-blue-700 text-white">
Hover me
</button>
{/* Focus */}
<input class="border focus:border-blue-500 focus:outline-none">
{/* Active */}
<button class="bg-gray-300 active:bg-gray-400">
Click me
</button>
{/* Dark mode */}
<div class="bg-white dark:bg-gray-800 text-black dark:text-white">
Supports dark mode
</div>
:bulb: Tip Common state prefixes:
hover:
- When mouse hovers over elementfocus:
- When element has keyboard focusactive:
- When element is being clickeddark:
- When dark mode is enableddisabled:
- When element is disabled
Let's build some real components with Tailwind:
<div class="max-w-sm mx-auto bg-white rounded-xl shadow-md overflow-hidden">
<img class="w-full h-48 object-cover" src="image.jpg" alt="Card image">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-800 mb-2">Card Title</h3>
<p class="text-gray-600 mb-4">Card description goes here...</p>
<button class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition">
Learn More
</button>
</div>
</div>
<nav class="bg-white shadow-lg">
<div class="container mx-auto px-4">
<div class="flex justify-between items-center py-4">
<div class="text-xl font-bold text-gray-800">Logo</div>
<div class="hidden md:flex space-x-6">
<a href="#" class="text-gray-600 hover:text-blue-500">Home</a>
<a href="#" class="text-gray-600 hover:text-blue-500">About</a>
<a href="#" class="text-gray-600 hover:text-blue-500">Services</a>
<a href="#" class="text-gray-600 hover:text-blue-500">Contact</a>
</div>
<button class="md:hidden">
<svg class="w-6 h-6" fill="none" stroke="currentColor">
{/* Hamburger icon */}
</svg>
</button>
</div>
</div>
</nav>
<section class="bg-gradient-to-r from-blue-500 to-purple-600 text-white">
<div class="container mx-auto px-4 py-24 text-center">
<h1 class="text-5xl font-bold mb-6">
Build Amazing Websites
</h1>
<p class="text-xl mb-8 max-w-2xl mx-auto">
Create stunning, responsive designs with Tailwind CSS
</p>
<div class="space-x-4">
<button class="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-gray-100 transition">
Get Started
</button>
<button class="border-2 border-white text-white px-8 py-3 rounded-lg font-semibold hover:bg-white hover:text-blue-600 transition">
Learn More
</button>
</div>
</div>
</section>
You can customize Tailwind to match your brand:
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {
colors: {
'brand-blue': '#1992d4',
'brand-purple': '#7e3af2',
},
fontFamily: {
'display': ['Oswald', 'sans-serif'],
'body': ['Inter', 'sans-serif'],
},
spacing: {
'128': '32rem',
'144': '36rem',
}
},
},
plugins: [],
}
:memo: Note After customizing, you can use your new utilities like
bg-brand-blue
orfont-display
in your HTML!
Let's compare the two approaches:
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Bootstrap Card</h5>
<p class="card-text">Pre-styled component</p>
<a href="#" class="btn btn-primary">Button</a>
</div>
</div>
</div>
</div>
</div>
<div class="container mx-auto">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-xl font-bold mb-2">Tailwind Card</h3>
<p class="text-gray-600 mb-4">Custom styled component</p>
<a href="#" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
Button
</a>
</div>
</div>
</div>
:bulb: Tip Key Differences:
- Bootstrap: Pre-designed components, faster to start, less customizable
- Tailwind: Build from scratch, more control, unique designs
When you use the same combination of utilities multiple times:
/* In your CSS file */
@layer components {
.btn-primary {
@apply bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
}
Keep your HTML readable:
{/* Use multiple lines for readability */}
<div class="
flex items-center justify-between
w-full max-w-4xl mx-auto
p-6 mb-8
bg-white rounded-lg shadow-lg
hover:shadow-xl transition-shadow
">
{/* Content */}
</div>
When patterns repeat, create reusable components:
{/* Create a component file or use a framework */}
<Card
title="Product Name"
description="Product description"
price="$99"
image="product.jpg"
/>
Build with Tailwind CSS assistance:
Get design inspiration:
Understand Tailwind concepts:
Troubleshoot Tailwind issues:
Create a modern product showcase page using only Tailwind CSS:
Challenge: No custom CSS allowed! Use only Tailwind utilities.
:bulb: Tip Start with the mobile design first, then add responsive classes for larger screens!
Tailwind CSS gives you:
With Tailwind, you can create any design you imagine using just utility classes!
:information_source: shadcn/ui is not a traditional component library. It's a collection of beautifully designed, accessible, and customizable components that you can copy and paste into your projects. Built on top of Tailwind CSS and Radix UI, it provides modern UI components without adding dependencies.
Let's see the difference:
// You're locked into their styling system
import { Button } from 'some-ui-library'
<Button variant="primary" size="large">
Click me
</Button>
// You own the code and can customize everything
// Copy the component into your project and modify as needed
<Button variant="default" size="lg">
Click me
</Button>
:memo: Note With shadcn/ui, you copy the component code into your project. You can then modify it however you want - it's YOUR code!
Since we're focusing on front-end only, let's adapt shadcn/ui components for vanilla HTML:
/* Add to your CSS file */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Let's explore some beautiful shadcn/ui components:
{/* Button variants */}
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2">
Default Button
</button>
{/* Secondary variant */}
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 px-4 py-2">
Secondary
</button>
{/* Destructive variant */}
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-destructive text-destructive-foreground hover:bg-destructive/90 h-10 px-4 py-2">
Delete
</button>
{/* With icon */}
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2">
<svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Add Item
</button>
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<div class="flex flex-col space-y-1.5 p-6">
<h3 class="text-2xl font-semibold leading-none tracking-tight">Card Title</h3>
<p class="text-sm text-muted-foreground">Card description goes here</p>
</div>
<div class="p-6 pt-0">
<p>Card content goes here. This is where you put your main content.</p>
</div>
<div class="flex items-center p-6 pt-0">
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2">
Action
</button>
</div>
</div>
{/* Text input */}
<input
type="text"
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Enter your name"
/>
{/* With label */}
<div class="space-y-2">
<label class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" for="email">
Email
</label>
<input
type="email"
id="email"
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Enter your email"
/>
</div>
{/* Default alert */}
<div class="relative w-full rounded-lg border p-4">
<h5 class="mb-1 font-medium leading-none tracking-tight">Heads up!</h5>
<div class="text-sm [&_p]:leading-relaxed">
You can add components to your app using the cli.
</div>
</div>
{/* Destructive alert */}
<div class="relative w-full rounded-lg border border-destructive/50 text-destructive p-4">
<h5 class="mb-1 font-medium leading-none tracking-tight">Error</h5>
<div class="text-sm [&_p]:leading-relaxed">
Your session has expired. Please log in again.
</div>
</div>
Let's combine these components to create a stunning product dashboard:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Dashboard with shadcn/ui</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
}
</style>
</head>
<body class="min-h-screen bg-background">
{/* Navigation */}
<nav class="border-b">
<div class="flex h-16 items-center px-4">
<div class="flex items-center space-x-4">
<h2 class="text-lg font-semibold">Dashboard</h2>
<nav class="flex items-center space-x-6 text-sm font-medium">
<a href="#" class="text-foreground/60 transition-colors hover:text-foreground">Overview</a>
<a href="#" class="text-foreground transition-colors hover:text-foreground/80">Products</a>
<a href="#" class="text-foreground/60 transition-colors hover:text-foreground">Customers</a>
</nav>
</div>
</div>
</nav>
{/* Main Content */}
<main class="flex-1 space-y-4 p-8 pt-6">
<div class="flex items-center justify-between space-y-2">
<h2 class="text-3xl font-bold tracking-tight">Products</h2>
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2">
Add Product
</button>
</div>
{/* Stats Cards */}
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<div class="p-6 flex flex-row items-center justify-between space-y-0 pb-2">
<h3 class="text-sm font-medium">Total Revenue</h3>
</div>
<div class="p-6 pt-0">
<div class="text-2xl font-bold">$45,231.89</div>
<p class="text-xs text-muted-foreground">+20.1% from last month</p>
</div>
</div>
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<div class="p-6 flex flex-row items-center justify-between space-y-0 pb-2">
<h3 class="text-sm font-medium">Sales</h3>
</div>
<div class="p-6 pt-0">
<div class="text-2xl font-bold">+2,350</div>
<p class="text-xs text-muted-foreground">+180% from last month</p>
</div>
</div>
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<div class="p-6 flex flex-row items-center justify-between space-y-0 pb-2">
<h3 class="text-sm font-medium">Active Products</h3>
</div>
<div class="p-6 pt-0">
<div class="text-2xl font-bold">24</div>
<p class="text-xs text-muted-foreground">+3 new this week</p>
</div>
</div>
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<div class="p-6 flex flex-row items-center justify-between space-y-0 pb-2">
<h3 class="text-sm font-medium">Active Users</h3>
</div>
<div class="p-6 pt-0">
<div class="text-2xl font-bold">+573</div>
<p class="text-xs text-muted-foreground">+201 from last hour</p>
</div>
</div>
</div>
</main>
</body>
</html>
shadcn/ui components come with:
Use the spacing scale:
{/* Use consistent spacing */}
<div class="space-y-4">
<div class="p-6">Content</div>
<div class="p-6">Content</div>
</div>
{/* Mobile-first approach */}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Cards */}
</div>
Build a modern SaaS landing page using shadcn/ui components:
Challenge: Make it fully responsive and accessible!
:bulb: Tip Start by setting up the CSS variables, then build each section one at a time. Test on mobile devices as you go!
shadcn/ui gives you:
Combined with Tailwind CSS, you can create stunning, production-ready interfaces that rival any modern web application!
Try these exercises to master Tailwind CSS:
Remember: The best way to learn Tailwind is by building real projects!
Next lesson: We'll explore Bootstrap and compare different approaches to CSS frameworks! :rocket: