In this tutorial, we'll build a real-world admin dashboard component using Tailwind CSS, based on the AdminOS template. You'll learn essential Tailwind concepts while creating something useful.
What We're Building
A responsive metric cards section showing key business metrics (MRR, Churn Rate, New Customers, ARPU) - perfect for any SaaS dashboard.
Prerequisites
- Basic HTML/CSS knowledge
- Tailwind CSS CDN or installed via npm
Step 1: Set Up Tailwind CSS
Add Tailwind to your HTML file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tailwind Dashboard Tutorial</title>
<script src="https://cdn.tailwindcss.com"></script>
<!-- Customize theme -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#0050cb',
}
}
}
}
</script>
</head>
<body class="bg-gray-50">
Step 2: Create the Metrics Grid Layout
Tailwind's grid system makes responsive layouts effortless:
<div class="max-w-7xl mx-auto px-4 py-8">
<!-- Grid: 1 column on mobile, 2 on tablet, 4 on desktop -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Metric cards will go here -->
</div>
</div>
Key Concepts:
-
grid-cols-1→ 1 column on mobile -
sm:grid-cols-2→ 2 columns on tablets (640px+) -
lg:grid-cols-4→ 4 columns on desktop (1024px+) -
gap-6→ 24px spacing between cards
Step 3: Build a Metric Card
Here's the first card showing MRR (Monthly Recurring Revenue):
<div class="bg-white rounded-xl border border-gray-200 p-6 shadow-sm hover:shadow-md transition-shadow">
<!-- Card header with icon -->
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 uppercase tracking-wide">MRR</p>
<div class="p-2 bg-blue-50 rounded-lg text-blue-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<!-- Metric value -->
<div class="flex flex-col">
<h3 class="text-2xl font-bold text-gray-900">$1.2M</h3>
<!-- Trend indicator -->
<div class="flex items-center gap-1 text-sm font-semibold text-emerald-600 mt-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
</svg>
<span>+12%</span>
<span class="text-gray-400 font-normal ml-1">vs last month</span>
</div>
</div>
</div>
What we learned:
-
bg-white+rounded-xl+border→ card styling -
shadow-sm+hover:shadow-md+transition-shadow→ interactive effects -
flex justify-between items-start→ horizontal layout with space between -
text-2xl font-bold→ typography scale -
text-emerald-600→ color coding for positive trends
Step 4: Create a Churn Rate Card (Negative Trend)
<div class="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 uppercase tracking-wide">Churn Rate</p>
<div class="p-2 bg-red-50 rounded-lg text-red-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
</div>
<div class="flex flex-col">
<h3 class="text-2xl font-bold text-gray-900">2.4%</h3>
<div class="flex items-center gap-1 text-sm font-semibold text-red-600 mt-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6"></path>
</svg>
<span>-0.5%</span>
<span class="text-gray-400 font-normal ml-1">vs last month</span>
</div>
</div>
</div>
Key Takeaway: Same card structure, different icon color (bg-red-50 + text-red-600) for negative metrics.
Step 5: Add Interactive Hover Effects
Let's enhance the cards with better interactivity:
<div class="group bg-white rounded-xl border border-gray-200 p-6 shadow-sm hover:shadow-lg transition-all duration-300 hover:-translate-y-1 cursor-pointer">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 uppercase tracking-wide group-hover:text-blue-600 transition-colors">
New Customers
</p>
<div class="p-2 bg-purple-50 rounded-lg text-purple-600 group-hover:scale-110 transition-transform">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
</svg>
</div>
</div>
<div class="flex flex-col">
<h3 class="text-2xl font-bold text-gray-900">450</h3>
<div class="flex items-center gap-1 text-sm font-semibold text-emerald-600 mt-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
</svg>
<span>82</span>
<span class="text-gray-400 font-normal ml-1">this week</span>
</div>
</div>
</div>
New Concepts:
-
group+group-hover:→ parent-triggered child animations -
hover:-translate-y-1→ lift effect on hover -
transition-all duration-300→ smooth animations -
group-hover:scale-110→ icon pops on card hover
Step 6: Add Dark Mode Support
Tailwind makes dark mode incredibly easy:
<html lang="en" class="dark">
<body class="bg-gray-50 dark:bg-gray-900">
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">ARPU</p>
<div class="p-2 bg-indigo-50 dark:bg-indigo-900/30 rounded-lg text-indigo-600 dark:text-indigo-400">
<!-- Icon SVG -->
</div>
</div>
<div class="flex flex-col">
<h3 class="text-2xl font-bold text-gray-900 dark:text-white">$85</h3>
<div class="flex items-center gap-1 text-sm font-semibold text-gray-500 dark:text-gray-400 mt-1">
<span>Stable</span>
<span class="text-gray-400 dark:text-gray-500 ml-1">per user</span>
</div>
</div>
</div>
</body>
</html>
Dark Mode Pattern:
-
dark:bg-gray-800→ dark background -
dark:text-white→ light text on dark -
dark:border-gray-700→ subtle borders - Always provide both light and dark variants
Step 7: Make It Responsive with Different Layouts
Here's how the grid adapts across all devices:
<!-- Responsive grid system -->
<div class="grid grid-cols-1
sm:grid-cols-2
lg:grid-cols-4
gap-4 sm:gap-6
p-4 sm:p-6 lg:p-8">
<!-- Cards maintain consistent internal spacing -->
<div class="bg-white rounded-xl p-4 sm:p-6">
<!-- `p-4` on mobile, `p-6` on desktop -->
</div>
</div>
<!-- Sidebar + Main layout pattern (like AdminOS) -->
<div class="flex flex-col md:flex-row min-h-screen">
<!-- Sidebar - hidden on mobile, shown on desktop -->
<aside class="md:w-64 bg-white dark:bg-gray-800 fixed md:static
inset-y-0 left-0 transform -translate-x-full
md:translate-x-0 transition-transform duration-300">
<!-- Sidebar content -->
</aside>
<!-- Main content - full width on mobile, with margin on desktop -->
<main class="flex-1 md:ml-64 p-4 md:p-6">
<!-- Dashboard content -->
</main>
</div>
Complete Working Example
Here's the complete HTML for our metric cards dashboard:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tailwind Dashboard Tutorial</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
primary: '#0050cb',
}
}
}
}
</script>
</head>
<body class="bg-gray-50 dark:bg-gray-900">
<div class="max-w-7xl mx-auto px-4 py-8">
<!-- Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Analytics Dashboard</h1>
<p class="text-gray-600 dark:text-gray-400 mt-2">Key metrics for Q4 2024</p>
</div>
<!-- Metrics Grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- MRR Card -->
<div class="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm hover:shadow-lg transition-all duration-300 hover:-translate-y-1">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">MRR</p>
<div class="p-2 bg-blue-50 dark:bg-blue-900/30 rounded-lg text-blue-600 dark:text-blue-400 group-hover:scale-110 transition-transform">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div>
<h3 class="text-2xl font-bold text-gray-900 dark:text-white">$1.2M</h3>
<div class="flex items-center gap-1 text-sm font-semibold text-emerald-600 dark:text-emerald-400 mt-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
</svg>
<span>+12%</span>
<span class="text-gray-400 dark:text-gray-500 font-normal ml-1">vs last month</span>
</div>
</div>
</div>
<!-- Churn Rate Card -->
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Churn Rate</p>
<div class="p-2 bg-red-50 dark:bg-red-900/30 rounded-lg text-red-600 dark:text-red-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
</div>
<div>
<h3 class="text-2xl font-bold text-gray-900 dark:text-white">2.4%</h3>
<div class="flex items-center gap-1 text-sm font-semibold text-red-600 dark:text-red-400 mt-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6"></path>
</svg>
<span>-0.5%</span>
<span class="text-gray-400 dark:text-gray-500 font-normal ml-1">vs last month</span>
</div>
</div>
</div>
<!-- New Customers Card -->
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">New Customers</p>
<div class="p-2 bg-purple-50 dark:bg-purple-900/30 rounded-lg text-purple-600 dark:text-purple-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
</svg>
</div>
</div>
<div>
<h3 class="text-2xl font-bold text-gray-900 dark:text-white">450</h3>
<div class="flex items-center gap-1 text-sm font-semibold text-emerald-600 dark:text-emerald-400 mt-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
</svg>
<span>82</span>
<span class="text-gray-400 dark:text-gray-500 font-normal ml-1">this week</span>
</div>
</div>
</div>
<!-- ARPU Card -->
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 shadow-sm">
<div class="flex justify-between items-start mb-4">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">ARPU</p>
<div class="p-2 bg-indigo-50 dark:bg-indigo-900/30 rounded-lg text-indigo-600 dark:text-indigo-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
</div>
</div>
<div>
<h3 class="text-2xl font-bold text-gray-900 dark:text-white">$85</h3>
<div class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mt-1">
<span>Stable</span>
<span class="text-gray-400 dark:text-gray-500 ml-1">per user</span>
</div>
</div>
</div>
</div>
<!-- Theme Toggle Button (Bonus) -->
<div class="fixed bottom-6 right-6">
<button onclick="document.documentElement.classList.toggle('dark')"
class="bg-white dark:bg-gray-800 p-3 rounded-full shadow-lg border border-gray-200 dark:border-gray-700">
<svg class="w-6 h-6 text-gray-800 dark:text-white dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
</svg>
<svg class="w-6 h-6 text-yellow-500 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
</button>
</div>
</div>
</body>
</html>
Key Takeaways from This Tutorial
- Utility-First Philosophy - Each class does one thing, combine them to build complex UIs
-
Responsive Prefixes -
sm:,md:,lg:control breakpoints -
Dark Mode - Just add
dark:variants anywhere -
State Variants -
hover:,focus:,group-hover:for interactivity -
Spacing Scale -
p-4,m-2,gap-6use a consistent 0.25rem ratio - Color System - Pre-defined palette with shades (gray-50 to gray-900)
What's Next?
- Add charts using Chart.js or Recharts
- Implement real data fetching with JavaScript
- Add more components: tables, forms, modals
- Build a complete dashboard like AdminOS
This tutorial was inspired by the AdminOS dashboard template from TemplatesJungle. For more complete templates and examples, visit templatesjungle.com/tailwind-css/.
Happy coding! 🚀
Top comments (0)